简体   繁体   中英

How to implement generic MVC web controllers and generic Views for CRUD operations using EntityFramework

I am working on the Design of an .NET MVC WEB application and have determined that I have to Manage 33 tables (so far). By Manage I mean the typical SQL operations (insert,update,delete and query)

16 out of those 33 tables are reference tables that need to be handled with the typical CRUD aproach. By reference table I mean tables that are reference by other tables and used in combos (ie coutries, states, cities, currencies, etc)

I am very new to MVC in .NET but have seen that when you scaffold you get Controllers that have the typical methods:

  • Index (get)
  • Details (get)
  • Create (get + post)
  • Edit (get + post)
  • Delete (get + post)

Also on the database side I plan to have the typical DAO that using EntityFramework implements the INSERT, UPDATE, DELETE and LIST (query operations)

Here are my questions (finally ;-)

Since 50% of my tables will be managed visually and functionally in the same way and offer the same CRUD operations is there a pattern or approach that I can use that achieves the best possible code/view reuse ? .

What I want to avoid is to scaffolding 16 entities to get 16 controllers each with 16*5 Views (Create, Delete, Details, Edit and Index)

Is it possible to have a Single Main controller that can route or manage all those 16 entities and calls the entity needed operations ?

Is it possible to have the a single set of Views (Create, Delete, Details, Edit and Index) that are generic enough to handle those same 16 entities ?

If the two above are possible how will I go about wiring the routing in the configuration of the web project to still be able to have specific URLs for each of those 16 entities (/Countries/Edit/5 , /States/Edit/5 and so on) ?

Here is one way that I think I can achieve this:

  • Have an Abstract/Common base class or interface for all those 16 Entities with the logic for the Save, GetAll and Delete database operations that manages the EntityFramework context (ie IManageData)
  • Have an EntityDao that using the EntityFramework calls the methods of any class that has implemented the IManageData and performs the operations.
  • Have a Abstract/Common base class (web controller) with the common logic of the CRUD operations of all the 16 entities that can call the methods of the IManageData

Please help me validate/complete my very draft design and mention some caveats or better approaches to achieve this.

Just to provide some context here is a link for the approch of scaffolding EntitieFramework entities with MVC project https://code.msdn.microsoft.com/MVC5-Demo-with-Entity-c6bc81df

Here is a very similar question with an accepted answer that I would like to validate before deciding on how to achieve this.

Generic CRUD controllers and views

I had similar situation on my previous project. I did it this way. Here is the general interface for every entity(table)

public interface IRepository<T>
{
    void Add(T entity, User initiator);
    void Update(T entity, User initiator);
    void Delete(T entity);
    void Delete(int entityId);
    IEnumerable<T> GetAll();
    T GetById(int entityId);
}

This general data layer can be easily implemented with Entity Framework(code sample for example here at the bottom) or using different approach(stored procedures as in my case)

I actually don't have any additional business logic, so my web project had direct access to data repository. So it is my generic base controller

public abstract class BaseController<T, M> : Controller
    where M : new()
{
    public BaseController(IRepository<T> repository)
    {
        this._repository = repository;
        ViewBag.CurrentUser = CurrentUser;
    }

    protected User CurrentUser
    {
        get
        {
            if (System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
            {
                return Mapper.Map<User>(System.Web.HttpContext.Current.User.Identity);
            }
            return null;
        }
    }

    protected virtual int PageSize
    {
        get { return 5; }
    }

    protected IRepository<T> _repository;

    public virtual ActionResult Index(int? currentPage)
    {
        var entities = _repository.GetAll();
        List<M> model = new List<M>();

        foreach (var currentEntity in entities)
        {
            model.Add(Mapper.Map<M>(currentEntity));
        }

        int pageNumber = (currentPage ?? 1);
        return View(model.ToPagedList(pageNumber, PageSize));
    }

    [HttpGet]
    public virtual ActionResult Add()
    {
        return View(new M());
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    [ValidateInput(false)]
    public virtual ActionResult Add(M model)
    {
        if (ModelState.IsValid)
        {
            _repository.Add(Mapper.Map<T>(model), CurrentUser);
            return RedirectToAction("Index");
        }

        return View(model);
    }

    [HttpGet]
    public virtual ActionResult Update(int modelId)
    {
        T domainModelEntity = _repository.GetById(modelId);
        M model = Mapper.Map<M>(domainModelEntity);

        return View(model);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    [ValidateInput(false)]
    public virtual ActionResult Update(M model)
    {
        if (ModelState.IsValid)
        {
            _repository.Update(Mapper.Map<T>(model), CurrentUser);
            return RedirectToAction("Index");
        }

        return View(model);
    }

    public virtual ActionResult Delete(int modelId)
    {
        _repository.Delete(modelId);
        return RedirectToAction("Index");
    }
}

And this specific controller

public class WebPagesController : BaseWebEntityController<WebPage, WebPageModel>
{
    public WebPagesController(IRepository<WebPage> repository)
        : base(repository)
    {
    }
}

A little explanation. a)You should use some IOC for controller dependency resolving.b)T is for entity from database(table) and M is for Model which is returned to View.c) Automapper is well known library.
I didn't make any changes to routing. Left it default.
Hope it helps. Good luck.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM