Error in adding custom table in user identity system - asp.net-mvc

I was following this article to add my custom table.
http://www.itorian.com/2013/11/customizing-users-profile-to-add-new.html
In My AccountViewModels.cs, i have tried to add new custom table(UserProfileInfo) something like this---
public class ApplicationUser : IdentityUser
{
public string EmailID { get; set; }
public virtual UserProfileInfo UserProfileInfo { get; set; }
}
public class UserProfileInfo
{
public int Id { get; set; }
public string City { get; set; }
public string MobileNum { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public System.Data.Entity.DbSet<UserProfileInfo> UserProfileInfo { get; set; }
}
}
and in my Account Controller's Register action (Post Version), i have tried to update register action like this but you can see in code at city and mobileNum,
its stating -----
xxx.RegisterViewModel' does not contain a definition for 'City' and no extension method 'City' accepting a first argument of type 'xxx.RegisterViewModel' could be found....
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName, EmailID = model.EmailID,
UserProfileInfo = new UserProfileInfo
{ City = model.City,
MobileNum = model.ModileNum
}
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
i dont know what's happening here.plzz help me out.thanks in advance

I have seen that article as u mentioned.You will never be able to pass City and mobile number as parameter as u have not defined them in register view model.
If u simply want to just create and another table and wants to save that into database then u can do it like this-------
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName };
user.HomeTown = model.HomeTown;
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
and change your Dbcontext to something like this----
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfileInfo> UserProfileInfo { get; set; }
}
and your Application user class should be something like this-----
public class ApplicationUser : IdentityUser
{
public string HomeTown { get; set; }
public virtual UserProfileInfo UserProfileInfo { get; set; }
}

You've created separated table - UserProfileInfo - that isn't part of ApplicationUser one.
What you've to do is:
crate new ApplicationUser
create new UserProfileInfo
link each other with proper navigation property or assign foreign keys manually (depending on your configuration)

Related

Complex custom user management identity asp.net core 3.1 running with breakpoints, and not running without breakpoints

I'm working on a new Visual Studio 2019 webapp project with asp.net core MVC 3.1, razor pages and scaffolded Identity.
I'm using a custom IdentityUser class
public class VivaceApplicationUser : IdentityUser
{
[PersonalData]
public string FirstName { get; set; }
[PersonalData]
public string LastName { get; set; }
//[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[DataType(DataType.Date)]
[PersonalData]
public DateTime DateOfBirth { get; set; }
[PersonalData]
public Address Address { get; set; }
[PersonalData]
public string AdditionaInfos { get; set; }
[PersonalData]
public LoginUserStatus Status { get; set; }
[PersonalData]
public bool hasPaidQuote { get; set; }
[DataType(DataType.Date)]
[PersonalData]
public DateTime paidOnDate { get; set; }
[DataType(DataType.Date)]
[PersonalData]
public DateTime paidValidity { get; set; }
[DataType(DataType.Date)]
[PersonalData]
public DateTime registeredSince { get; set; }
}
public enum LoginUserStatus
{
Submitted,
Approved,
Rejected
}
This class use an Address class defined as follows:
public class Address
{
public int AddressID { get; set; }
public string City { get; set; }
public int PostalCode { get; set; }
public string Street { get; set; }
public string CivicNumber { get; set; }
}
My ApplicationDbContext class looks like that:
public class ApplicationDbContext : IdentityDbContext<VivaceApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
I can login in my webapp and create a new user.
In the database I can see the new user and his address as well (in the address table).
In the dbo.AspNetUsers I can also see the right AdressID.
My issue is now in deleting a user in the DeletePersonalDataModel class.
public class DeletePersonalDataModel : PageModel
{
private readonly UserManager<VivaceApplicationUser> _userManager;
private readonly SignInManager<VivaceApplicationUser> _signInManager;
private readonly ILogger<DeletePersonalDataModel> _logger;
private readonly ApplicationDbContext _context;
public DeletePersonalDataModel(
UserManager<VivaceApplicationUser> userManager,
SignInManager<VivaceApplicationUser> signInManager,
ILogger<DeletePersonalDataModel> logger,
ApplicationDbContext context)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
_context = context;
}
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
public Address Address { get; set; }
}
public bool RequirePassword { get; set; }
public async Task<IActionResult> OnGet()
{
// Get user
var user = await _userManager.GetUserAsync(User);
// Get user from _context
var userFromContext = await _context.Users.FindAsync(user.Id);
// Get user address from _context
var addressFromContext = await _context.Addresses.FindAsync(user.Address.AddressID);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (addressFromContext == null)
{
return NotFound($"Unable to load user address of user with ID '{_userManager.GetUserId(User)}'.");
}
RequirePassword = await _userManager.HasPasswordAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
// Get user
var user = await _userManager.GetUserAsync(User);
// Get user address from _context
var addressFromContext = await _context.Addresses.FindAsync(user.Address.AddressID);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (addressFromContext == null)
{
return NotFound($"Unable to load user address with ID '{_userManager.GetUserId(User)}'.");
}
RequirePassword = await _userManager.HasPasswordAsync(user);
if (RequirePassword)
{
if (!await _userManager.CheckPasswordAsync(user, Input.Password))
{
ModelState.AddModelError(string.Empty, "Incorrect password.");
return Page();
}
}
var result = await _userManager.DeleteAsync(user);
var resultRemoveAddress = _context.Addresses.Remove(addressFromContext);
var userId = await _userManager.GetUserIdAsync(user);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'.");
}
//if (!resultRemoveAddress.)
//{
// throw new InvalidOperationException($"Unexpected error occurred deleting user address of user with ID '{userId}'.");
//}
await _context.SaveChangesAsync();
await _signInManager.SignOutAsync();
_logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
return Redirect("~/");
}
}
I can get the current (logged) user from the _userManager class in the OnGet() method
// Get user
var user = await _userManager.GetUserAsync(User);
I can also get the current (logged) user from the _contect class in the OnGet() method
// Get user from _context
var userFromContext = await _context.Users.FindAsync(user.Id);
And I can get the user's address from the _context class as well in the OnGet() method
// Get user address from _context
var addressFromContext = await _context.Addresses.FindAsync(user.Address.AddressID);
Setting a breakpoint on
// Get user
var user = await _userManager.GetUserAsync(User);
in the OnGet() method and waiting a couple of seconds I see this
But if I run without breakpoints I get an unhandled exception
What I'm doing wrong? And how to solve this issue?
If I run with breakpoints the database gets updated correctly. Both the user and his address get deleted.
Thanks very much!

Can't access foreignkey data of applicationUser, 'ICollection' does not contain a definition for 'Message'

Trying to add/Edit related data properties of applicationUser so notes can be recorded for users
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
public virtual ICollection<UserNote> UserNotes { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
}
UserNote.cs
public class UserNote
{
[Key]
public int UserNoteId { get; set; }
public string Message { get; set; }
public string ApplicationUserID { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
Controller
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(string id, ApplicationUser applicationUser)
{
if (ModelState.IsValid)
{
var userFromDb = _db.ApplicationUser
.Include(n => n.UserNotes)
.Include(r => r.UserRoles)
.AsNoTracking().FirstOrDefault(m => m.Id == id);
UserNote note = _db.UserNote.Single(x => x.UserNoteId == 1);
userFromDb.LastName = applicationUser.LastName;
userFromDb.FirstName = applicationUser.FirstName;
//Error accessing related data properties
userFromDb.UserNotes.Message = applicationUser.UserNotes.Message;
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(applicationUser);
}
I received 'ICollection' does not contain a definition for 'Message' and no accessible extension method 'Message' accepting a first argument of type 'ICollection'
UserNotes is a collection hence you need to access a certain index before getting its Message property.
// Use .Count to check if the list contains any number.
if(applicationUser.UserNotes.Count != 0){
// .FirstOrDefault() will return the first object in that list, if there is none, it would return null.
userFromDb.UserNotes.FirstOrDefault().Message = applicationUser.UserNotes.FirstOrDefault().Message;
}else{
// this is just to let you know if it's empty just in case if you don't debug
return Content("Your collection is empty.");
}
You could also use .IndexOf() or .Where() with your list if you're looking for a specific record.
It's not entirely clear to me with what you're trying, if you're adding a new note or you're editing an existing note. Let me know further in the comments if this doesn't work for you.
Try to edit the related data properties of applicationUser as follows :
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(string id, ApplicationUser applicationUser)
{
if (ModelState.IsValid)
{
var userFromDb = _context.ApplicationUser
.Include(n => n.UserNotes)
.AsNoTracking().FirstOrDefault(m => m.Id == id);
//UserNote note = _context.UserNote.Single(x => x.UserNoteId == 1);
userFromDb.LastName = applicationUser.LastName;
userFromDb.FirstName = applicationUser.FirstName;
foreach (var usernote in applicationUser.UserNotes.ToList())
{
if (userFromDb.UserNotes.Any(un => un.UserNoteId == usernote.UserNoteId))
{
userFromDb.UserNotes.Find(un => un.UserNoteId == usernote.UserNoteId).Message = usernote.Message;
}
}
_context.Update(userFromDb);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(applicationUser);
}

Don't make username unique asp.net identity

I want to implement custom logic for username validation. Created function ValidateEntity for username custom validation but if I provide unique username while creating user then ValidateEntity function is hit and If I provide duplicate username then this function is not hit.
IdentityModel.cs
public class ApplicationUser : IdentityUser
{
public int AppId { get; set; }
//other attributes
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
if ((entityEntry != null) && (entityEntry.State == EntityState.Added))
{
var user = entityEntry.Entity as ApplicationUser;
//custom logic for username validation
}
return base.ValidateEntity(entityEntry, items);
}
}
In AccountController.cs
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password); //shouldn't it always goto ValidateEntity function?
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
return View(model);
}
Update:
I added public new string UserName { get; set; } and now I get error Name cannot be null or empty here is the screen shot of data.
It is not good practice for the username field not to be unique, unless you plan to cover that in your custom validator. However you should be able to override the username field in the ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public int AppId { get; set; }
// Override username field
public new string UserName { get; set; }
//other attributes
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
Additionally, if your database already exists, you must also then remember to remove the unique index UserNameIndex for everything to work.

mvc 5 code first userid as a Foreign Key - How to save data

Using MVC5 code first approach.
I haven't changed the user store, added a property called Name in ApplicationUser Class. So it looks like as follows : -
public class ApplicationUser : IdentityUser
{
[Required]
[StringLength(100)]
public string Name { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
I have a company class, which looks like below :-
public class Company
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
}
A company will definitely has more than one user, but in my design a user can be associated with more than one company. So I have a separate class for CompanyUsers which looks as follows :-
public class CompanyUsers
{
public int Id { get; set; }
[Required]
public virtual Company Company { get; set; }
[Required]
public virtual ApplicationUser User { get; set; }
}
Now, when I register a user, I'm setting its role and also companies that the user belongs to, but seems like the UserManager defined in the built-in code has already saved it. So after adding CompanyUsers data, when I call applicationDbContext.Savechanges() it fails as the user has already been saved in the database. Can you tell me how I can achieve it? I can do it in some other logic, like after creating a user, in a separate function assign users to company(s), but I want to know the solution in this circumstances. Also I can redesign the user store.
My modified register function in AccountController is as follows :-
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, Name = model.Name };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await UserManager.AddToRoleAsync(user.Id, model.Role);
var userCompany = new CompanyUsers
{
Company = _context.Companies.First(t => t.Id == model.CompanyId),
User = user
};
try
{
_context.CompanyUsers.Add(userCompany);
_context.SaveChanges();// throws exception
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
}
return RedirectToAction("UserViewAll", "Manage");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
I have just started mvc code first approach, so I may not understand if you use technical jargon, thanks In Advance for your help!!!!!
Try adding the following code in the if (result.Succeeded) block:
_context.Entry(user).State = EntityState.Unchanged;
The issue appears to be that the data context in this method does not know the state of the user entity because UserManager.CreateAsync uses a separate context. The entity is attached when you assign it to the CompanyUser entity, and then this data context tries to save it as a new entity.

Data Not Saved after click on submit button - MVC 5

After click on the sumbit button and trying to debug the code i don't recieve any error or exception but the data not saved
I have a class called login ... need the user after click on login button save its data in the database
This is my Post Action:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(Login model, string returnUrl)
{
Context db = new Context();
if (string.IsNullOrEmpty(model.LoginName))
{
this.ModelState.AddModelError("LoginName", "this field is required");
return this.View(model);
}
try
{
if (ModelState.IsValid)
{
var user = new Login { LoginName = model.LoginName, LoginPassword = model.LoginPassword };
db.Logins.Add(user);
db.SaveChanges();
return Redirect("http://www.google.com");
}
else
return View();
}
catch (Exception e)
{
return View();
}
}
And this is my Login Class:
[Table("Login")]
public partial class Login
{
public int ID { get; set; }
[StringLength(50)]
public string LoginName { get; set; }
[StringLength(10)]
public string LoginPassword { get; set; }
}
And this is my DBContext:
public class LoginContext : DbContext
{
public LoginContext () : base("LoginContext ")
{
}
public virtual DbSet<Login> Logins { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
After return to the google link the data not saved.
After trying a lot of solution Finally i found that :
When adding the Code first code i had make the DBContext with the
same name of the table name "Login".
Delete all of them then add the new code first "DBContext" and
choose your DB.
Will find that the DBConext will added automatically and your tables also will added.
That will work correctly.

Resources