Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use ASP.NET MVC Generic Controller to populate right model

Tags:

c#

asp.net-mvc

I asked a question about ASP.NET MVC Generic Controller and this answer shows a controller like this:

public abstract class GenericController<T> 
    where T : class
{
    public virtual ActionResult Details(int id)
    {
        var model = _repository.Set<T>().Find(id);
        return View(model);
    }
}

Which can be implemented like this.

public class FooController : GenericController<Foo>
{

}

Now when someone requests /Foo/Details/42, the entitiy is pulled from the _repository's Set<Foo>(), without having to write anything for that in the FooController.

The way he explain that is good but think i want to develop a generic controller for product and customer where i will not use EF to load product & customer model rather use MS data access application block.

public abstract class GenericController<T> 
        where T : class
    {
        public virtual ActionResult Details(int id)
        {
            //var model = _repository.Set<T>().Find(id);
            var model =customer.load(id);
            or 
            var model =product.load(id);
            return View(model);
        }
    }

So when request comes like /Customer/Details/42 or /product/Details/11 then generic controller's details method will call but how we can detect that request comes from which controller and accordingly instantiate right class to load right model.

If request comes for Customer then I need to load customer details from detail action method or if request comes for Product then I need to load Product details from detail action method of generic controller.

How do I use generics to get the dataset of type T with the Entity Framework Data block?

like image 719
Mou Avatar asked Nov 15 '13 19:11

Mou


People also ask

How do you pass model value to view from a controller?

The other way of passing the data from Controller to View can be by passing an object of the model class to the View. Erase the code of ViewData and pass the object of model class in return view. Import the binding object of model class at the top of Index View and access the properties by @Model.

How pass data from model to controller in MVC?

In Solution Explorer, right-click the Controllers folder and then click Add, then Controller. In the Add Scaffold dialog box, click MVC 5 Controller with views, using Entity Framework, and then click Add. Select Movie (MvcMovie. Models) for the Model class.


3 Answers

You may create a set of repositories for working with your entities, like CustomerRepository, ProductRepository from base interface like

public interface IBaseRepository
{
  T Get<T>(int id);
  void Save<T>(T entity);
}

and then extend your base controller class with repository type and its instance with any of DI frameworks

public abstract class GenericController<T, TRepo> 
        where T : class
        where TRepo : IBaseRepository, new()
    {
        private IBaseRepository repository;

        public GenericController() 
        {
            repository = new TRepo();
        }

        public virtual ActionResult Details(int id)
        {
           var model =repository.Get<T>(id);
           return View(model);
        }
    }

Example for CustomerController

public class CustomerController : GenericController<Customer, CustomerRepository>
{

}

where CustomerRepository:

public class CustomerRepository : IBaseRepository
{
  public T Get <T>(int id) 
  {
    // load data from DB
    return new Customer();
  }
}
like image 53
Dmytro Rudenko Avatar answered Oct 02 '22 01:10

Dmytro Rudenko


I don't think it's wise to place data-access and business logic like this in controllers when your application's size and complexity grows beyond a certain point. You should create repositories which handle the data-access and abstract the technology (EF, plain ADO.NET, etc.) away from the consumers. You could use these repositories in your controller, but that would mean that your controllers still contain business logic which you don't want. Controllers should be thin.

What I did was creating a service layer between my repositories and controllers which contain the business logic and delegate data-access to the repositories. I use these services in my controllers to fetch my domain models where I map them to view models. You're gonna want an Inversion of Control container to 'glue' the layers together and to provide loose coupling between them.

A search for 'c# mvc repository and service pattern' will result in loads of examples. I found this post a good one, except for the fact that he returns view models from his services rather than domain models.

This is just my 2 cents, please keep in mind that all of the above only counts when your have a 'mid-range' application and not a typical tutorial/try-out website.

like image 39
Henk Mollema Avatar answered Oct 02 '22 02:10

Henk Mollema


Given my disclaimers in the other question and my comments here explaining why this isn't an ultimate solution, I'll try to give a more concrete implementation:

public abstract class GenericController<T> : Controller
    where T : class
{
    protected YourEFContext _dataSource;

    public GenericController()
    {
        _dataSource = new YourEFContext();
    }

    public virtual ActionResult Details(int id)
    {
        var model = _dataSource.Set<T>().Find(id);
        return View(model);
    }
}

public class CustomerController : GenericController<Customer>
{

}

This is all code that is needed to let /Customers/Details/42 load customer with ID 42 be loaded from the Entity Framework context. The "generic" part is solved by Entity Framework's DbContext.Set<T>() method, which returns the DbSet<TEntity> for the appropriate entity, in this case DbSet<Customer> which you can query.

That being said, there are many problems with actually using this code:

  • You don't want to let your controller know about your data access. As you see, a YourEFContext property is used in the controller, tightly coupling it with Entity Framework. You'll want to abstract this away in a repository pattern.
  • You don't want your controller to instantiate its data access, this should be injected.
  • You don't want your controller to return database entities. You're looking for ViewModels and a Mapper.
  • You don't want your controller to do data access. Move the data access in a service layer that also contains your business logic, abstract it again through a repository pattern.

Now your question actually is "Does the Enterprise Library Data Block have a method like GetDataSet<T>", so you don't have to refer to customer and product in your generic controller, but unfortunately I can't find that as I haven't used EntLib for a few years. It would help if you show the code you currently use to access your database.

The ultimate goal you're looking for:

[       MVC             ] <=> [         Service         ] <=> [   Repository   ]
View ViewModel Controller     BusinessModel BusinessLogic     DataModel Database

Your controller only talks to your service to Create/Read/Update/Delete BusinessModels, and performs the mapping between ViewModels and BusinessModels (at <=>). The service contains the business logic and business model (DataContacts when using WCF) and in turn maps (<=>) to and from DataModels and talks to your repository to persist your models.

I understand this can be a bit much to grasp at once, and that's probably why most ASP.NET MVC tutorials start with all three tiers in one application. Take a look at ProDinner for a more proper approach.

like image 32
CodeCaster Avatar answered Oct 02 '22 01:10

CodeCaster