8

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?

Community
  • 1
  • 1
Mou
  • 15,673
  • 43
  • 156
  • 275
  • What's the goal of a generic controller? – Stinky Towel Nov 15 '13 at 20:04
  • You will need to show the code where you initialize the Data Block in a controller that currently works. The thing with generics is that you _don't_ access a model by its type; you let your repository return it. That's what you can use Entity Frameworks' `DbContext.Set()` method for, so your generic controller _doesn't_ know at compile time which concrete model it'll return. We can show an example for Data Block, but that is done the easiest if you show your current code. – CodeCaster Nov 16 '13 at 10:58
  • @StinkyTowel save code. clean code. no need to re-write the same exact code for 100 controllers (for 100 entities). Just inherit and use. you get uniformity, clean code, easy maintenance and more. – LongChalk Feb 15 '23 at 13:18

3 Answers3

13

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();
  }
}
Bellash
  • 7,560
  • 6
  • 53
  • 86
Dmytro Rudenko
  • 2,524
  • 2
  • 13
  • 22
  • thanks for the answer but can u give me full smaple code for product & customer....but i like to mention that i will not use EF rather i would use ado.net for db interaction. thanks – Mou Nov 16 '13 at 10:01
  • It doesn't matter how you will implement IBaseRepository realization. Main point is product and customer controllers, defined with concrete entity type and repository type. For example if you have CustomerRepository : IBaseRepository for your Customer entity, you should only have public class CustomerContoller : GenericController {} definition for controller class. – Dmytro Rudenko Nov 16 '13 at 10:10
  • what is the main objective of generic controller.......what advantage we get by this....please explain it if possible. – Monojit Sarkar Jun 18 '17 at 23:03
8

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.

Community
  • 1
  • 1
Henk Mollema
  • 44,194
  • 12
  • 93
  • 104
  • To Henk you listen, hmmm! – Stinky Towel Nov 15 '13 at 19:59
  • that is good idea but my knowledge is not good about repositories design patter but like to know how could i inform my repositories to load customer data when people request this url /Customer/Details/42 ? or load product data when /product/Details/11. can u guide me. can u give me any article for my scenario. thanks – Mou Nov 15 '13 at 20:03
  • @Mou check out the URL I mentioned in my answer, it gives a detailed example. – Henk Mollema Nov 15 '13 at 20:08
  • 2
    I wrote the example and I agree with that you should not directly use an ORM in your controller. This is however how every MVC tutorial works, so it is the easiest way to move forward. There are plenty of examples and tutorials on the web that explain how to move your data access to a service layer and let your controller talk to the service layer instead of the data layer. This service layer can however also be implemented as a generic repository, enabling you to yet again use a generic controller. – CodeCaster Nov 16 '13 at 10:55
  • what is the main objective of generic controller.......what advantage we get by this....please explain it if possible. – Monojit Sarkar Jun 18 '17 at 23:03
5

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.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272