Reputation: 95
In my "Car Hire" solution I have a separate project that holds all my entities such as User,Role, Car, Booking etc. I am using EntityFramework Code First to generate the database. All of that is working fine. I have added a MVC project to my solution. Now I want to take full advantage of the ASP.NET Identity 2.x model. My question is: how can I incorporate the ApplicationUser that comes with the MVC project with my own User Class? My User class:
public partial class User
{
public User()
{
}
[Key]
public int Id { get; set; }
[StringLength(200)]
public string FirstName { get; set; }
[StringLength(200)]
public string LastName { get; set; }
[StringLength(200)]
public string Address1 { get; set; }
[StringLength(200)]
public string Address2 { get; set; }
[StringLength(200)]
public string Address3 { get; set; }
[StringLength(20)]
public string ZipCode { get; set; }
[StringLength(200)]
public string City { get; set; }
[StringLength(200)]
public string Country { get; set; }
[StringLength(200)]
public string Phone { get; set; }
[StringLength(200)]
public string MobilePhone { get; set; }
[StringLength(200)]
public string WorkPhone { get; set; }
}
Should I move all of my User properties to the ApplicationUser class and add a foreign key to ApplicationUser in my own classes such as the Booking class ?
Upvotes: 2
Views: 2799
Reputation: 228
This will be the base class for all you users
public abstract class User : IdentityUser
{
public abstract string Area { get; }
public bool IsActiveDirectoryUser { get; private set; }
protected User(string username, bool isActiveDirectoryUser = false)
: base(username)
{
IsActiveDirectoryUser = isActiveDirectoryUser;
}
protected User()
{ }
}
This is an example of user
public class AdminUser : User
{
public AdminUser(string username, bool isActiveDirectoryUser = false)
: base(username, isActiveDirectoryUser)
{ }
private AdminUser()
{ }
public override string Area
{
get { return UserAreas.Admin; }
}
}
This is the DBContext, with the mappings ignoring the user are property because It is hard coded
public class IdentityDataContext : IdentityDbContext<User>
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().ToTable("AspNetUsers").Ignore(u => u.Area);
modelBuilder.Entity<AdminUser>().ToTable("AdminUsers");
}
}
This is your custom implementation of IUserManager. It works over the entity User, using the IdentityDataContext we already defined and our custom ClaimsFactory if nesesary as shown below
public class UserManager
{
private readonly UserManager<User> _identityManager;
public UserManager(ClaimsFactory claimsFactory, IdentityDataContext context, IdentityValidator identityValidator)
{
_identityManager = new UserManager<User>(new UserStore<User>(context))
{
ClaimsIdentityFactory = claimsFactory,
UserValidator = identityValidator
};
}
public void Register(User user, string password)
{
var result = _identityManager.Create(user, password);
if (!result.Succeeded)
throw new ApplicationException("User can not be created. " + result.Errors.FirstOrDefault());
}
public void Register(User user)
{
var result = _identityManager.Create(user);
if (!result.Succeeded)
throw new ApplicationException("User can not be created. " + result.Errors.FirstOrDefault());
}
public User Find(string userName, string password)
{
return _identityManager.Find(userName, password);
}
public ClaimsIdentity CreateIdentity(User user, string applicationCookie)
{
return _identityManager.CreateIdentity(user, applicationCookie);
}
public User FindByUserName(string userName)
{
return _identityManager.FindByName(userName);
}
public bool ChangePassword(string identifier, string oldPassword, string newPassword)
{
return _identityManager.ChangePassword(identifier, oldPassword, newPassword).Succeeded;
}
public bool ResetPassword(string userName, string password)
{
try
{
var user = FindByUserName(userName);
var result = _identityManager.RemovePassword(user.Id);
if (result != IdentityResult.Success)
return false;
result = _identityManager.AddPassword(user.Id, password);
return result == IdentityResult.Success;
}
catch (Exception)
{
return false;
}
}
public User FindById(string userId)
{
return _identityManager.FindById(userId);
}
public bool IsInRole(string userId, string role)
{
return _identityManager.IsInRole(userId, role);
}
public void AddToRole(string userId, string role)
{
_identityManager.AddToRole(userId, role);
}
}
If you like to have claims, this is the claims factory. It converts de user area into a claim, and finally a cookie in the browser.
public class ClaimsFactory : ClaimsIdentityFactory<User>
{
public async override Task<ClaimsIdentity> CreateAsync(UserManager<User> manager, User user, string authenticationType)
{
var identity = await base.CreateAsync(manager, user, authenticationType);
identity.AddClaim(new Claim(ClaimTypes.Area, user.Area, ClaimValueTypes.String));
return identity;
}
}
Upvotes: 1
Reputation: 239200
There's nothing special about ApplicationUser
. It's just a default implementation of a class that inherits from IdentityUser
. Even then, that inheritance is merely to bootstrap all the functionality in Identity such as roles, claims, etc.
You can either remove ApplicationUser
entirely and make your user class inherit from IdentityUser
or you can move the properties from your user class to ApplicationUser
and remove your user class. Either way, the result will be the same.
And, yes, you want to create foreign keys between your classes that are related to a "user" and whatever class ends up being your "user" class. Again, there's nothing really special about ApplicationUser
, so if you choose to stick with that, you can set it up like any other POCO in your project, creating relationships with other entities as needed.
Some have suggested inheriting your user class from ApplicationUser
, but I don't think that's a great idea. There are valid reasons to inherit from the base "user" class used for Identity, but here you're not dealing with different types of users, but rather one user type that needs additional properties. Inheritance is really only an acceptable way to achieve that if you have no access to the base class to modify it. Here, you do. It's your class; the project template merely created it for you.
Upvotes: 2
Reputation: 438
But hey, could you only make a link from ApplicationUser to your User model?
Sometimes it's not so easy to move code like that. If that's the case you could do something like this:
public class ApplicationUser {
public virtual YourUserModel userModel { get; set; }
}
Hope it helps!
EDIT: Or, you could do as Callum said, make your User class inherit from IdentityUser:
public class User : IdentityUser {
//your properties here
}
Upvotes: 0