如何使用ASP.NET MVC通用控制器来填充正确的模型
我问了一个关于ASP.NET MVC Generic Controller的问题, 这个答案显示了一个像这样的控制器:
public abstract class GenericController where T : class { public virtual ActionResult Details(int id) { var model = _repository.Set().Find(id); return View(model); } }
哪个可以像这样实现。
public class FooController : GenericController { }
现在当有人请求/ Foo / Details / 42时,权限从_repository的
Set()
提取,而不必在FooController
中为其写入任何FooController
。
他解释的方式很好,但我认为我想为产品和客户开发一个通用控制器,我不会使用EF来加载产品和客户模型,而是使用MS数据访问应用程序块。
public abstract class GenericController where T : class { public virtual ActionResult Details(int id) { //var model = _repository.Set().Find(id); var model =customer.load(id); or var model =product.load(id); return View(model); } }
因此,当请求来自/Customer/Details/42 or /product/Details/11
通用控制器的详细信息方法将调用,但我们如何检测该请求来自哪个控制器并相应地实例化右类以加载正确的模型。
如果请求来自客户,那么我需要从详细操作方法加载客户详细信息,或者如果请求来自产品,那么我需要从通用控制器的详细操作方法加载产品详细信息。
如何使用generics来获取带有entity framework数据块的类型T
的数据集?
您可以创建一组存储库以便与您的实体一起使用,例如CustomerRepository
, ProductRepository
from base interface like
public interface IBaseRepository { T Get(int id); void Save (T entity); }
然后使用任何DI框架扩展具有存储库类型及其实例的基本控制器类
public abstract class GenericController 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(id); return View(model); } }
CustomerController
示例
public class CustomerController : GenericController { }
在哪里CustomerRepository:
public class CustomerRepository : IBaseRepository { public T Get (int id) { // load data from DB return new Customer(); } }
当您的应用程序的大小和复杂性超出某一点时,我不认为将这样的数据访问和业务逻辑放在控制器中是明智的。 您应该创建处理数据访问的存储库,并从消费者那里抽象出技术(EF,普通ADO.NET等)。 您可以在控制器中使用这些存储库,但这意味着您的控制器仍然包含您不需要的业务逻辑。 控制器应该很薄。
我所做的是在我的存储库和控制器之间创建一个服务层,其中包含业务逻辑和委托对存储库的数据访问。 我在我的控制器中使用这些服务来获取我的域模型,我将它们映射到视图模型。 你需要一个Inversion of Control容器来将这些层“粘合”在一起并在它们之间提供松散的耦合。
搜索“c#mvc存储库和服务模式”将导致大量示例。 我发现这篇文章很好,除了他从他的服务而不是域模型返回视图模型的事实。
这只是我的2美分,请记住,以上所有内容仅适用于您拥有“中档”应用程序而非典型的教程/试用网站。
鉴于我在另一个问题中的免责声明以及我在这里的评论解释了为什么这不是一个终极解决方案,我将尝试给出一个更具体的实现:
public abstract class GenericController : Controller where T : class { protected YourEFContext _dataSource; public GenericController() { _dataSource = new YourEFContext(); } public virtual ActionResult Details(int id) { var model = _dataSource.Set ().Find(id); return View(model); } } public class CustomerController : GenericController { }
这是从Entity Framework上下文中加载/Customers/Details/42
加载ID为42
客户所需的所有代码。 “通用”部分由Entity Framework的DbContext.Set
方法解决,该方法返回适当实体的DbSet
,在本例中为DbSet
,您可以查询。
话虽如此,实际使用此代码存在许多问题:
- 您不希望让控制器知道您的数据访问权限。 如您所见,在控制器中使用了
YourEFContext
属性,将其与Entity Framework紧密耦合。 您将要在存储库模式中抽象出来。 - 您不希望控制器实例化其数据访问,应该注入。
- 您不希望控制器返回数据库实体 。 您正在寻找ViewModels和Mapper。
- 您不希望控制器进行数据访问 。 在包含业务逻辑的服务层中移动数据访问,再通过存储库模式对其进行抽象。
现在您的问题实际上是“企业库数据块是否有像GetDataSet
这样的方法” ,因此您不必在通用控制器中引用customer
和product
,但遗憾的是我无法找到它,因为我没有几年来一直使用EntLib。 如果您显示当前用于访问数据库的代码,将会有所帮助。
您正在寻找的最终目标:
[ MVC ] <=> [ Service ] <=> [ Repository ] View ViewModel Controller BusinessModel BusinessLogic DataModel Database
您的控制器仅与您的服务进行通信,以创建/读取/更新/删除BusinessModel,并执行ViewModel和BusinessModels之间的映射(位于<=>
)。 该服务包含业务逻辑和业务模型(使用WCF时的DataContacts),然后映射( <=>
)与DataModels之间的映射,并与存储库进行对话以保留模型。
我知道这可以立即掌握,这可能是大多数ASP.NET MVC教程从一个应用程序中的所有三个层开始的原因。 看看ProDinner的更合适的方法。