如何使用ASP.NET MVC通用控制器来填充正确的模型

时间:2013-11-15 19:26:58

标签: c# asp.net-mvc

我问了一个关于ASP.NET MVC通用控制器的问题,而this answer显示了这样一个控制器:

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

可以像这样实施。

public class FooController : GenericController<Foo>
{

}
     

现在当有人请求/ Foo / Details / 42时,权限将从_存储库的Set<Foo>()中提取,而不必在FooController中为其写入任何内容。

他解释的方式很好,但我想我想为产品和客户开发通用控制器,我不会使用EF来加载产品和产品。客户模型而不是使用MS数据访问应用程序块。

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);
        }
    }

因此,当请求来自/Customer/Details/42 or /product/Details/11时,通用控制器的详细信息方法将调用,但我们如何检测该请求来自哪个控制器并相应地实例化右类以加载正确的模型。

如果请求来自客户,那么我需要从详细操作方法加载客户详细信息,或者如果请求来自产品,那么我需要从通用控制器的详细操作方法加载产品详细信息。

如何使用泛型来获取带有实体框架数据块的T类型的数据集?

3 个答案:

答案 0 :(得分:12)

您可以创建一组存储库,以便与您的实体一起使用,例如来自基础界面的CustomerRepositoryProductRepository,例如

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

然后使用任何DI框架

扩展具有存储库类型及其实例的基本控制器类
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);
        }
    }

CustomerController

的示例
public class CustomerController : GenericController<Customer, CustomerRepository>
{

}

CustomerRepository:

public class CustomerRepository : IBaseRepository
{
  public T Get <T>(int id) 
  {
    // load data from DB
    return new Customer();
  }
}

答案 1 :(得分:7)

当您的应用程序的大小和复杂性超出某一点时,我不认为将这样的数据访问和业务逻辑放在控制器中是明智的。您应该创建处理数据访问的存储库,并从消费者那里抽象出技术(EF,普通ADO.NET等)。您可以在控制器中使用这些存储库,但这意味着您的控制器仍然包含您不想要的业务逻辑。控制器应该很薄。

我所做的是在我的存储库和控制器之间创建一个服务层,其中包含业务逻辑和委托对存储库的数据访问。我在我的控制器中使用这些服务来获取我的域模型,我将它们映射到视图模型。你需要一个Inversion of Control容器来将这些层“粘合”在一起,并在它们之间提供松耦合。

搜索“c#mvc存储​​库和服务模式”将导致大量示例。我发现this post是一个很好的,除了他从他的服务而不是域模型返回视图模型这一事实。

这只是我的2美分,请记住,以上所有内容仅适用于您拥有“中档”应用程序而非典型的教程/试用网站。

答案 2 :(得分:3)

鉴于我在另一个问题中的免责声明以及我在这里的评论解释了为什么这不是最终的解决方案,我将尝试提供更具体的实施:

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>
{

}

这是让/Customers/Details/42加载从实体框架上下文加载ID 42的客户所需的所有代码。 “通用”部分由实体框架的DbContext.Set<T>()方法解决,该方法返回适当实体的DbSet<TEntity>,在这种情况下DbSet<Customer>可以查询。

话虽如此,实际使用此代码存在许多问题:

  • 您不希望让控制器知道您的数据访问权限。如您所见,控制器中使用了YourEFContext属性,将其与Entity Framework紧密耦合。您将要在存储库模式中抽象出来。
  • 您不希望您的控制器实例化其数据访问权限,这应该被注入。
  • 您不希望控制器返回数据库实体。您正在寻找ViewModels和Mapper。
  • 您不希望控制器进行数据访问。在包含业务逻辑的服务层中移动数据访问,再通过存储库模式对其进行抽象。

现在您的问题实际上是“企业库数据块是否有类似GetDataSet<T>的方法,因此您不必参考{您的通用控制器中有{1}}和customer,但遗憾的是我无法找到,因为我已经使用过EntLib几年了。如果您显示当前用于访问数据库的代码,将会有所帮助。

您正在寻找的最终目标:

product

您的控制器仅与您的服务进行通信,以创建/读取/更新/删除BusinessModel,并执行ViewModel和BusinessModels之间的映射([ MVC ] <=> [ Service ] <=> [ Repository ] View ViewModel Controller BusinessModel BusinessLogic DataModel Database )。该服务包含业务逻辑和业务模型(使用WCF时的DataContacts),然后将数据映射(<=>)与DataModels进行对话,并与存储库进行对话以保留模型。

据我所知,这可能会立即掌握,这可能是大多数ASP.NET MVC教程从一个应用程序中的所有三个层开始的原因。请查看ProDinner以获得更合适的方法。