It's not perfect to implement UnitOfWork as a static class. Define an interface IUnitOfWork. Your db context will implement this interface. It may look like:
public interface IUnitOfWork {
int SaveChanges();
}
public class EFDbContext: DbContext, IUnitOfWork {
public DbSet<User> User { get; set; }
public EFDbContext(string connectionString)
: base(connectionString) { }
public override int SaveChanges() {
return base.SaveChanges();
}
}
I usually create several repositories inherited from generic repository. Thus names of repository find methods could have more specific names. This also prevents from duplication of repository logic in different controllers.
For example:
public class EFUserRepository: EFRepository<User>, IUserRepository {
public EFUserRepository(IUnitOfWork context)
: base(context) { }
protected override DbSet<User> Table {
get { return Context.User; }
}
public User Find(string email) {
return Table.FirstOrDefault(u => u.Email == email);
}
public bool Validate(string email, string password) {
string passwordHash = Cryptography.GenerateHash(password);
User user = Find(email);
return user != null && user.Password == passwordHash;
}
Now about controller: To simplify testing it's better to use IoC Container, e.g. NInject So the dependencies between controller <-> repo <-> unitOfWork will be resolved by NInject.
This how could look UserController with Login method:
public class UserController: Controller {
[Ninject.Inject]
public IUserRepository UserRepository { get; set; }
public ActionResult Login(AuthorizationViewModel vm) {
if(ModelState.IsValid) {
if(UserRepository.Validate(vm.Email, vm.Password)) {
FormsAuthentication.SetAuthCookie(vm.Email, true);
if(Url.IsLocalUrl(vm.ReturnUrl)) {
return Redirect(vm.ReturnUrl);
}
else {
return RedirectToAction("Page", "Main");
}
}
else {
ModelState.AddModelError("", Resources.Validation.WrongEmailOrPassword);
}
}
return View(vm);
}
}
The dependencies resolving could be done by custom controller factory, like this:
public class NInjectControllerFactory: DefaultControllerFactory {
public IKernel Kernel { get; private set; }
public NInjectControllerFactory() {
Kernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) {
return controllerType == null
? null
: (IController)Kernel.Get(controllerType);
}
private void AddBindings() {
Kernel.Bind<IUnitOfWork>().To(typeof(EFDbContext)).InRequestScope();
Kernel.Bind<IUserRepository>().To(typeof(EFUserRepository).InRequestScope();
}
}
And replace current controller factory with your custom. You can do it in Application_Start handler of Global.asax:
protected void Application_Start() {
...
ControllerBuilder.Current.SetControllerFactory(new NInjectControllerFactory());
}
If you decide to use NInject, you can simply add it with Nuget. To enable binding InRequestScope you also need NInject.Web.Common. Of course, there a lot of other alternatives like Castle Windsor or StructureMap, but NInject is the simplest one.
Hope it will help.