I'm new to MVC and have been pulling my hair out with this one. I'm already going bold so need someone to rescue what's left!
I'm using asp.net Identity and trying to create a one-to-one relationship between RegisterViewModel and StoreDetails and really hope someone can help me out!
Here is my RegisterViewModel:
public class RegisterViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public virtual StoreDetails StoreDetails { get; set; }
}
Here is my StoreDetails model:
public class StoreDetails
{
[Key, ForeignKey("RegisterViewModel")]
public string StoreName { get; set; }
public virtual RegisterViewModel RegisterViewModel { get; set; }
}
Controller:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser {
// PERSONAL DETAILS
UserName = model.Email,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName
};
var storeDetails = new StoreDetails {
// STORE DETAILS
StoreName = model.StoreDetails.StoreName
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
// UPDATE DB WITH STORE DETAILS DATA
var db = new ApplicationDbContext();
db.StoreDetails.Add(new StoreDetails
{
StoreName = model.StoreDetails.StoreName
});
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
What i'm looking to achieve is on registration the StoreDetails table is generated with the following input:
#Html.TextBoxFor(m => m.StoreDetails.StoreName)
If you need any further details please ask.
Thanks
RegisterViewModel is not a real entity i.e person that gets saved into the database by EF. All it is is a carrier object that passes the values of email and password into the real User/person object called ApplicationUser. This is a view-model and not a real model.
in your example you added StoreDetails in the RegisterViewModel which is correct. But you also need to add this in your ApplicationUser class as a property.
public class ApplicationUser {
public virtual StoreDetails StoreDetails { get; set; }
Then run your migrations.
Adding anything to the RegisterViewModel is made to pass data from the view layer in a way to safe guard the object thats going into your db.
i.e
doing this doesnt do anything because the viewmodel doesnt live in the db
public class StoreDetails
{
[Key, ForeignKey("RegisterViewModel")] //this will not work remove
public string StoreName { get; set; }
public virtual RegisterViewModel RegisterViewModel { get; set; }// remove
}
public Ienumerable<StoreDetails> StoreDetails { get; set; }
public int StoreDetailsId { get; set; }
In controller:
using (var db = ApplicationDbContext.Create())
{
model.StoreDetails = db.StoreDetails.ToList();
}
Related
So I'm working on an api controller and I have an update method like this:
[HttpPost("update")]
public async Task<IActionResult> Update([FromBody] UpdateModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = _userManager.Users
.Include(u => u.Address)
.FirstOrDefault(u => u.UserName == model.Email);
if (user == null)
{
return BadRequest(new {Message = "User doesn't exist"});
}
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.Address = model.Address;
await _userManager.UpdateAsync(user);
return Ok(new {Message = "User has been updated successfully"});
}
When updating the user through an api call, the user gets updated, except for the address.
This is the address:
public class Address : Entity
{
public string AddressLine { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
}
And the update model:
public class UpdateModel
{
[Required]
[MinLength(3)]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
public Address Address { get; set; }
}
I've done proper migrations, and I tried setting a breakpoint on the user variable. The variable indeeds hold an Address before updateAsync is called.
The user's firstName and lastName both get updated in a POST request, but the address remains null.
Does anyone know why this would happen?
I think you are set one to one mapping between User & Address. So first of all check the relation mapping & still not work then try to make new variable of Address and after that assign this new variable to user.Address
Successfully I added some fields in AspNetUsers table but I don't know how I can retrieve them. My project is asp.net web api and I can retrieve username from token. I want to return departmentId by given userId, or username.
and trying like but this gives me null exeption
private ApplicationUserManager _userManager;
public int GetdeptId(string username)
{
var dept = _userManager.Users.Where(d => d.UserName == username).Select(c => c.DepartmentId).FirstOrDefault();
return dept;
}
Here is the way I could create fields in my AspNetUsers and I can insert values successfully. The problem is to return values from AspNetUsers.
public class RegisterBindingModel
{
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[Display(Name = "FirstName")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Lastname")]
public string Lastname { get; set; }
[Required]
[Display(Name = "Department")]
public int DepartmentId { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
And then the registerration
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email, Firstname = model.FirstName, LastName = model.Lastname, DepartmentId = model.DepartmentId };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
Thank you in advance!
Try!
var context = new IdentityDbContext();
var users = context.Users.ToList();
If you have access to your ApplicationDbContext as say, _db, then you can cast it to IdentityDbContext and access the tables through that. e.g.
IdentityDbContext context = _db as IdentityDbContext;
IEnumerable<User> users = context.Users;
I am fairly new to MVC5/ASP.NET Identity and I have found an issue that has stumped me a bit.
I am writing a small form for my ASP.NET MVC5 application that will allow an admin user (member of the Admins role) to review the users that have signed up to the site and edit the details of and assign roles to those users. When the form is submitted, if a role has been assigned to the user the UserManager.AddToRole method is called.
I noticed after this that once this is done, that user is then unable to log into the application. Looking in the database, it appears when AddToRole is called, the PasswordHash field is set to null. Is this normal and if not how to I get around this issue?
For reference my relevant code is below
Controller
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Admin")]
public ActionResult Details(EditUserViewModel model)
{
model.Save();
return RedirectToAction("List", "Account");
}
Relevant view models
public class EditUserViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
[Required]
[StringLength(255)]
[Display(Name = "Email address")]
public string Email { get; set; }
[StringLength(255)]
[Display(Name = "First name")]
public string FirstName { get; set; }
[StringLength(255)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[StringLength(255)]
[Display(Name = "Mobile Number")]
public string MobileNumber { get; set; }
public IList<EditUserRolesViewModel> UserRoles { get; set; }
public void Save()
{
using (ApplicationDbContext context = new ApplicationDbContext())
{
ApplicationUser user = new ApplicationUser()
{
Id = this.UserId,
UserName = this.UserName,
Email = this.Email,
FirstName = this.FirstName,
LastName = this.LastName,
MobileNumber = this.MobileNumber
};
context.Users.Attach(user);
context.Entry(user).Property(x => x.Email).IsModified = true;
context.Entry(user).Property(x => x.FirstName).IsModified = true;
context.Entry(user).Property(x => x.LastName).IsModified = true;
context.Entry(user).Property(x => x.MobileNumber).IsModified = true;
context.SaveChanges();
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
foreach (var row in this.UserRoles)
{
if (row.RowChanged)
{
if (row.RoleAssigned)
{
UserManager.AddToRole(this.UserId, row.RoleName);
}
else
{
UserManager.RemoveFromRole(this.UserId, row.RoleName);
}
}
}
}
}
}
public class EditUserRolesViewModel
{
public string RoleId { get; set; }
[Display(Name = "Role name")]
public string RoleName { get; set; }
[Display(Name = "Assigned")]
public bool RoleAssigned { get; set; }
public bool RowChanged { get; set; }
}
As I see from your code, you have attached partially initialized object user to context.Users. As a result when UserManager gets control: AddToRole, it tries to update Database. And you'll have lot empty or null fields in the current users row.
You can fix doing any of the following (both will help):
instead of
ApplicationUser user = new ApplicationUser() use user = UserManager.FindById(UserId)
after assigning values from viewmodel EntityFramework will take care of modified fields.
use another context when dealing with roles
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
I have set Identity so I have separate table for user profile data (firstname, lastname etc.).
Problem is that I have joined those tables via Email field.
I want to change this so I can connect tables by UserID instead.
What I need is to get new userID for created user and use that as foreign key to UserProfileInfo object.
public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public string Email { get; set; }
public string ConfirmationToken { get; set; }
public bool IsConfirmed { get; set; }
public virtual UserProfileInfo UserProfileInfo { get; set; }
}
public class UserProfileInfo
{
public int Id { get; set; }
public string EmailId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
When I create new user I have:
var user = new ApplicationUser()
{
UserName = model.UserName,
Email = model.Email,
ConfirmationToken = confirmationToken,
IsConfirmed = false,
UserProfileInfo = new UserProfileInfo { EmailId = model.Email }
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
SendEmailConfirmation(model.Email, model.UserName, confirmationToken);
return RedirectToAction("RegisterStepTwo", "Account");
}
else...
Why are you doing a join in the first place? ApplicationUser is your user, and the whole point of inheriting from IdentityUser is to allow you extend the user object. Just put your properties like FirstName and LastName directly on ApplicationUser.
I'm working with ASP.NET MVC4 and now I want to add a dropdownlist with data from my mysql database. This is what I do :
In my view (Register.cshtml):`
<div class="control-group">
#Html.LabelFor(m => m.DistrictID, new { #class= "control-label"})
<div class="controls">
#Html.DropDownListFor(model => model.DistrictID, new SelectList(ViewBag.Districts, "district_id", "district_name", Model.DistrictID))
</div>
</div>
In my Controller (AccountController):
[AllowAnonymous]
public ActionResult Register()
{
var districts = repository.GetDistricts();
ViewBag.Districts = districts;
return View(new RegisterModel());
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the User
try
{
MembershipService.CreateUser(model.Name, model.FamilyName, model.BirthDate, model.Sex, model.Nationality, model.Email, model.UserName, model.Password, model.Street, model.StreetNr);
FormsAuthentication.SetAuthCookie(model.UserName, false);
return RedirectToAction("Index", "Home");
}
catch (ArgumentException ae)
{
ModelState.AddModelError("", ae.Message);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
I get the Districts from my Repository like this:
public IQueryable<district> GetDistricts()
{
return from district in entities.districts
orderby district.district_name
select district;
}
My RegisterModel:
public class RegisterModel
{
[Required]
[Display(Name = "Given name")]
public string Name { get; set; }
[Required]
[Display(Name = "Family name")]
public string FamilyName { get; set; }
[Required]
[Display(Name = "Birthdate")]
public DateTime BirthDate { get; set; }
[Required]
[Display(Name = "Sex")]
public string Sex { get; set; }
[Required]
[Display(Name = "Nationality")]
public string Nationality { get; set; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
[Display(Name = "Street")]
public string Street { get; set; }
[Required]
[Display(Name = "Street Number")]
public int StreetNr { get; set; }
[Required]
[Display(Name = "District")]
public IEnumerable<SelectListItem> Districts { get; set; }
public int DistrictID { get; set; }
}
The dropdownlist is filled with districts but when I click on "Register" I get this error:
Value cannot be null.
Parameter name: items
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: items
When I debug the method I see that the ModelState is NOT valid.
Key 11 is DistrictID and includes the districtid but Key 12 is Districts and gives the error: "The district field is required" ...
What am I doing wrong?
Consider the case when model validation fails. View will be redisplayed again with model sent with the request. However this line:
new SelectList(ViewBag.Districts, "district_id", "district_name", Model.Districts)
will have null as a first parameter, since ViewBag.Districts was not repopulated, causing the exception. So in order to avoid exception just set this property again:
// If we got this far, something failed, redisplay form
var districts = repository.GetDistricts();
ViewBag.Districts = districts;
return View(model);
Update. When seeing the model definition, thing that immediately comes into the mind is Required attribute of the Districts collection. Most likely you do not need user to enter any of those, nor you save them into the database. Try removing this attribute, and error about this property will disappear.