asp.net-mvc-3 – EF和存储库模式 – 在一个控制器中以多个DbCon
我对ASP.NET MVC 3的大部分知识来自阅读Adam Freeman和Steven Senderson撰写的Pro ASP.NET MVC 3 Framework一书.对于我的测试应用程序,我试图非常密切地坚持他们的例子.我正在使用存储库模式加上Ninject和Moq,这意味着单元测试工作得很好(即无需从数据库中提取数据).
在本书中,存储库的使用方式如下: public class EFDbTestChildRepository { private EFDbContext context = new EFDbContext(); public IQueryable<TestChild> TestChildren { get { return context.TestChildren; } } public void SaveTestChild(TestChild testChild) { if (testChild.TestChildID == 0) { context.TestChildren.Add(testChild); } else { context.Entry(testChild).State = EntityState.Modified; } context.SaveChanges(); } } 这是与之相关的DbContext: public class EFDbContext : DbContext { public DbSet<TestParent> TestParents { get; set; } public DbSet<TestChild> TestChildren { get; set; } } 请注意:为了简化这个提取的例子,我在这里省略了接口ITestChildRepository,然后Ninject会使用它. 在其他来源中,我看到了一个更通用的存储库方法,其中一个存储库足以满足整个应用程序的需要.显然在我的情况下,我最终在我的应用程序中找到了相当多的存储库列表 – 基本上我的域模型中的每个实体都有一个.不确定这两种方法的优缺点 – 我只是按照这本书来保证安全. 最后得出我的问题:每个存储库都有自己的DbContext – 私有EFDbContext context = new EFDbContext();.我是否有冒险在一个请求中使用多个DbContexts的风险?这会导致任何重大的性能开销吗?如何在上下文之间发生冲突以及对数据完整性的任何后果? 这是一个例子,我最终在控制器中有多个存储库. 我的两个数据库表与外键关系链接.我的域模型类: public class TestParent { public int TestParentID { get; set; } public string Name { get; set; } public string Comment { get; set; } public virtual ICollection<TestChild> TestChildren { get; set; } } public class TestChild { public int TestChildID { get; set; } public int TestParentID { get; set; } public string Name { get; set; } public string Comment { get; set; } public virtual TestParent TestParent { get; set; } } Web应用程序包含一个允许用户创建新TestChild的页面.在它上面有一个选择框,其中包含可供选择的TestParents列表.这就是我的控制器的样子: public class ChildController : Controller { private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository(); private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository(); public ActionResult List() { return View(testChildRepository.TestChildren); } public ViewResult Edit(int testChildID) { ChildViewModel cvm = new ChildViewModel(); cvm.TestChild = testChildRepository.TestChildren.First(tc => tc.TestChildID == testChildID); cvm.TestParents = testParentRepository.TestParents; return View(cvm); } public ViewResult Create() { ChildViewModel cvm = new ChildViewModel(); cvm.TestChild = new TestChild(); cvm.TestParents = testParentRepository.TestParents; return View("Edit",cvm); } [HttpPost] public ActionResult Edit(TestChild testChild) { try { if (ModelState.IsValid) { testChildRepository.SaveTestChild(testChild); TempData["message"] = string.Format("Changes to test child have been saved: {0} (ID = {1})",testChild.Name,testChild.TestChildID); return RedirectToAction("List"); } } catch (DataException) { //Log the error (add a variable name after DataException) ModelState.AddModelError("","Unable to save changes. Try again,and if the problem persists see your system administrator."); } // something wrong with the data values return View(testChild); } } 有一个EFDbTestChildRepository是不够的,但我还需要一个EFDbTestParentRepository.它们都被分配给控制器的私有变量 – 瞧,在我看来,已经创建了两个DbContexts.或者这不正确? 为了避免这个问题,我尝试使用EFDbTestChildRepository来访问TestParents.但这显然只会带来那些已经连接到至少一个TestChild的人 – 所以不是我想要的. 以下是视图模型的代码: public class ChildViewModel { public TestChild TestChild { get; set; } public IQueryable<TestParent> TestParents { get; set; } } 如果我忘记包含一些相关代码,请告诉我.非常感谢您的建议! 解决方法
不存在性能问题(除非我们讨论纳秒,实例化上下文非常便宜)并且您不会损坏您的数据完整性(在此之前您将获得异常).
但这种方法非常有限,只能在非常简单的情况下使用.在许多情况下,多个上下文会导致问题.例如:假设您要为现有父级创建一个新子级,并使用以下代码尝试: var parent = parentRepo.TestParents.Single(p => p.Id == 1); var child = new Child { TestParent = parent }; childrenRepo.SaveTestChild(child); 这个简单的代码不起作用,因为parent已经附加到parentRepo中的上下文,但childrenRepo.SaveTestChild将尝试将它附加到childrenRepo内部的上下文中,这将导致异常,因为实体不能附加到另一个上下文. (这实际上是一种解决方法,因为你可以设置FK属性而不是加载父:child.TestParentID = 1.但是如果没有FK属性,那将是一个问题.) 如何解决这样的问题? 一种方法可以是通过新属性扩展EFDbTestChildRepository: public IQueryable<TestParent> TestParents { get { return context.TestParents; } } 在上面的示例代码中,您可以只使用一个存储库,代码可以工作.但正如您所看到的,名称“EFDbTest Child Repository”不再适合新存储库的用途.它现在应该是“EFDbTest ParentAndChild Repository”. 我将其称为聚合根方法,这意味着您创建一个存储库不仅仅针对一个实体,而是针对少数几个彼此密切相关且在它们之间具有导航属性的实体. 另一种解决方案是将上下文注入存储库(而不是在存储库中创建它),以确保每个存储库使用相同的上下文. (上下文通常被抽象为IUnitOfWork接口.)示例: public class MyController : Controller { private readonly MyContext _context; public MyController() { _context = new MyContext(); } public ActionResult SomeAction(...) { var parentRepo = new EFDbTestParentRepository(_context); var childRepo = new EFDbTestChildRepository(_context); //... } protected override void Dispose(bool disposing) { _context.Dispose(); base.Dispose(disposing); } } 这为您提供了可在多个存储库中使用的每个控制器的单个上下文. 下一步可能是通过依赖注入为每个请求创建单个上下文,例如…… private readonly MyContext _context; public MyController(MyContext context) { _context = context; } …然后配置IOC容器以创建单个上下文实例,该实例可能被注入到多个控制器中. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net – 是否可以访问位于另一个项目的MVC视图?
- asp.net – 部署DLL时IIS初始加载速度太慢
- asp.net-mvc – ToDataSourceResult扩展不填充DataSourceRe
- asp.net-mvc – 使用MVC FileStream播放视频文件的问题
- asp.net-mvc – ASP.NET MVC实体框架关系绑定
- asp.net-mvc – 如何在ASP.NET MVC中实现分页?
- 如何检查密码答案是否与ASP.Net中用户提供的密码相匹配
- asp.net – 静态文件上的间歇性401和302错误
- asp.net-mvc – NSubstitute:在MVC / Web Api控制器中模拟
- asp.net-mvc-3 – 从MVC 3中使用Razor View引擎的局部视图渲
- 如何使用ASP.NET MVC实现站点而不使用Visual Stu
- ASP.NET Mvc Api:设置cookie然后302/303 Redire
- 为什么我不能在我的代码asp.net c#中使用app_cod
- 来自ASP.NET MVC站点的“无效的JSON原语:alihac
- 动手造轮子:实现一个简单的 AOP 框架
- asp.net-mvc-3 – Asp.Net MVC 3 – @ Html.Acti
- asp.net – 获取数据绑定到ListView上DataBound事
- asp.net – 在我的代码隐藏类中,如何检索授权角色
- asp.net-mvc-4 – 如何为OData服务配置Upshot.js
- .net – 有什么简单的方法可以将标准转换为HQL吗