I was wondering whether it is a good aproach to have Identity 2.0 UserManager
next to UnitOfWork
pattern? I am using UserManager
only in login and register as it gives freedom of not taking care of password hashing (also provides options to email user etc.) and user-to-role management. Although for the rest of the project I am using UOW
to maintain users and roles (e.g. to edit user information (not password).
These are my Login
and Register
methods:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult >Login(UserLoginViewModel model, string returnUrl)
{
if (!this.ModelState.IsValid)
{
return this.View();
}
var user = (await this.unitOfWork.UserRepository.Find(u =>
u.UserName == model.Login || u.UstId == model.Login)).FirstOrDefault();
if (user == null)
{
this.ModelState.AddModelError("Login", Properties.Error.UserDoesntExist);
return this.View();
}
var userManager = this.HttpContext.GetOwinContext().GetUserManager<LeaveUserManager>();
var authManager = this.HttpContext.GetOwinContext().Authentication;
if (!await userManager.CheckPasswordAsync(user, model.Password))
{
this.ModelState.AddModelError("Password", Properties.Error.WrongPassword);
return this.View();
}
var ident = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
authManager.SignIn(new AuthenticationProperties { IsPersistent = false }, ident);
this.TempData["SuccessMessage"] = "Logged in";
if (!string.IsNullOrEmpty(returnUrl) && this.Url.IsLocalUrl(returnUrl))
{
return this.Redirect(returnUrl);
}
return this.RedirectToAction("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(UserRegisterViewModel model)
{
if (!this.ModelState.IsValid)
{
return this.View();
}
var userManager = this.HttpContext.GetOwinContext().GetUserManager<LeaveUserManager>();
var user = new User(model);
var result = userManager.Create(user, model.Password);
if (!result.Succeeded)
{
result.Errors.ForEach(e => this.ModelState.AddModelError(string.Empty, e));
return this.View();
}
var userRole = this.unitOfWork.UserRoleRepository.Find(ur => ur.Name == "User").Result.FirstOrDefault();
if (userRole == null)
{
userRole = new UserRole("User");
this.unitOfWork.UserRoleRepository.Add(userRole);
this.unitOfWork.Complete();
}
userManager.AddToRole(user.Id, "User");
this.TempData["SuccessMessage"] = "Registered";
return this.RedirectToAction("Index");
}
And assigning user to role also uses UserManager
:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Assign(AssignRoleViewModel model)
{
if (!this.ModelState.IsValid)
{
return this.View(model);
}
var userManager = this.HttpContext.GetOwinContext().GetUserManager<LeaveUserManager>();
userManager.RemoveFromRoles(model.Id, userManager.GetRoles(model.Id).ToArray());
if (model.SelectedRoles.Any())
{
userManager.AddToRoles(model.Id, model.SelectedRoles.ToArray());
}
return this.RedirectToAction("Details", new { id = model.Id });
}
Although Edit method uses uow
:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(User model)
{
if (this.ModelState.IsValid)
{
var user = await this.unitOfWork.UserRepository.Get(model.Id);
user.EditUser(model);
await this.unitOfWork.Complete();
return this.RedirectToAction("Index");
}
var userList = this.unitOfWork.UserRepository.GetAll().Result;
this.ViewBag.SuperiorId = new SelectList(userList, "Id", "FirstName", model.SuperiorId);
return this.View(model);
}
And my UserManager
:
public class LeaveUserManager : UserManager<User>
{
public LeaveUserManager(IUserStore<User> store) : base(store)
{
}
public static LeaveUserManager Create(
IdentityFactoryOptions<LeaveUserManager> options, IOwinContext context)
{
var manager = new LeaveUserManager(
new UserStore<User>(context.Get<VacationsContext>()))
{
EmailService = new LeaveEmailService()
};
return manager;
}
}
Is it OK to leave it like this or I should create my own userManager which will be using uow and repositories?
I am using ASP.NET MVC5 with EF6.
2018-03-20 edit:
I have removed .Result
from all async methods and replaced with await
.
I have dependency injected all managers
, dbcontext
and uow
using Ninject
library.
private static void RegisterService(IKernel kernel)
{
kernel.Bind<VacationsContext>().ToSelf().InSingletonScope();
kernel.Bind<RoleManager<UserRole>>().ToSelf().InRequestScope();
kernel.Bind<LeaveUserManager>().ToMethod(c => LeaveUserManager.Create(kernel.Get<VacationsContext>())).InRequestScope();
kernel.Bind<IAuthenticationManager>().ToMethod(c => HttpContext.Current.GetOwinContext().Authentication).InRequestScope();
kernel.Bind<IUserStore<User>>().To<UserStore<User>>().InRequestScope().WithConstructorArgument("context", kernel.Get<VacationsContext>());
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
}
Injection works in controllers:
public HomeController(LeaveUserManager userManager, IAuthenticationManager authManager, IUnitOfWork unitOfWork)
{
this.leaveUserManager = userManager;
this.authenticationManager = authManager;
this.unitOfWork = unitOfWork;
}
And if noone says that it is some kind of violation - then I will leave LeaveUserManager
as it is - using it's own way to database access layer (skipping my custom uow).
UserManager
is more of an implementation concern that you can encapsulate behind your own abstraction in whatever structure/pattern you want. \$\endgroup\$.Result
within async methods as those blocking calls can lead to deadlocks \$\endgroup\$