Tuesday, January 12, 2010

Using Generic CRUD Controllers in ASP.NET MVC. part 1

Since making some projects on ASP.NET MVC, i got a question – why  generic repositories is widespread, but generic controllers is not.

What is controller? In DDD Architecture it just dispatcher between other subsystems. And in similar scenarios ( like is CRUD ), way of dispatching will be identical. For example: typical Edit Action:

        public ActionResult Edit(int id)
        {
            Entity entity = _repository.Get(id);
            if (entity == null) return RedirectToAction("ObjectNotFound");
            if (!_security.CanView(entity, LoggedUser)) return RedirectToAction("ObjectNotFound");
            return View("Edit", "Personal", mapper.to<ViewModel>( entity ) );        
        }
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(ViewModel model)
        {
            Entity entity = model.EntityId.HasValue ? _repository.Get(model.EntityId.Value) : null;
            if (entity == null) return RedirectToAction("ObjectNotFound");
            if (!_security.CanUpdate(entity, LoggedUser)) return RedirectToAction("NotEnoughPermissions");
            if ( !entity.IsValid )                
             return View("Edit", "Personal", mapper.to<ViewModel>( entity ) );
            else  
            using (ITransaction tr = NHibernateSession.Current.BeginTransaction())
            {
                entity = _service.Update(entity, model, LoggedUser);
                _repository.SaveOrUpdate(entity);
                tr.Commit();
                return RedirectToAction("View", new { id = entity.Id, username = LoggedUser.Login });
            }
        }


All lines is Generic  for most controllers. All that is necessary -  extract generic types in base class and specify it in concrete realization. Also we need some constrains:



 public abstract class CrudController<TEntity, TRepository, TService, TViewModel> :
        PersonalPostController<TEntity, TRepository, TService, TViewModel>
        where TEntity : Entity
        where TRepository : IRepository<TEntity>
        where TService : IBaseEntityService<TEntity,TViewModel>
        where TViewModel: EntityViewModel<TEnity> - optional
    {
    protected CrudController(TRepository repository, TService service )
        {
        _repository = repository;
        _service = service;
        }
..........
    public ActionResult Edit(int id)
        {
            TEntity entity = _repository.Get(id);
            if (entity == null) return RedirectToAction("ObjectNotFound");
            if (!_security.CanView(entity, LoggedUser)) return RedirectToAction("ObjectNotFound");
            return View("Edit", "Personal", mapper.to<TViewModel>( entity ) );
        }
        
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(TViewModel model)
        {
            TEntity entity = model.EntityId.HasValue ? _repository.Get(model.EntityId.Value) : null;
            if (entity == null) return RedirectToAction("ObjectNotFound");
            if (!_security.CanUpdate(entity, LoggedUser)) return RedirectToAction("NotEnoughPermissions");
            if ( !entity.IsValid )                
             return View("Edit", "Personal", mapper.to<TViewModel>( entity ) );
            else  
            using (ITransaction tr = NHibernateSession.Current.BeginTransaction())
            {
                entity = _service.Update(entity, model, LoggedUser);
                _repository.SaveOrUpdate(entity);
                tr.Commit();
                return RedirectToAction("View", new { id = entity.Id, username = LoggedUser.Login });
            }
        }
    }


For each entity we have just follow:



public class UsersControlles:CrudConrtoller<User, IUserRepository, IUserService, UserViewModel>
{
    public UsersControlles( IUserRepository repository, IUserService service ): base(repository, service)
       {}
}


 



in Next part i plan show how can do some strategies with different types of entities and go deep into TService implementation

1 comment:

  1. Nice article.

    However, you could do with Generic views too, so you should restrict your CRUD to always return to the View "Edit" in "Personal" controller .

    Once again, congratulations.

    ReplyDelete