ren*_*zol 5 c# asp.net-mvc entity-framework dbcontext asp.net-mvc-4
摘要
这个问题是关于方法论的。答案应该是在针对所描述的场景的上下文中与圣杯的链接。
我们在MVC Web应用程序项目中遇到了与dbContext的使用相关的不同问题。
在阅读了许多问答博客,文章……(包括具有存储库和注入模式的提案,Owin,Entity Framework,Ninject)之后,我们仍然不清楚使用dbContext的正确方法。
在MVVC表示层/域实体/逻辑/数据访问层之间进行分隔(包括身份安全处理用户和角色权限)时,是否有任何文章,演示以及与“ CRUD”操作相比更复杂的应用程序中使用“方法”的方法?
描述
以前,我们的方法是在每个存储库中需要时创建dbContext对象。很快,由于连接与存储库功能一起消失,我们发现了诸如“ dbContext已处置”之类的错误。这使得检索到的对象“部分可用”到应用程序的上层(使用技巧.ToList(),这是有限的,因为我们可以访问集合和属性,但以后不能导航到对象子表,依此类推)。同样使用来自不同存储库的2个上下文,我们得到了一个异常,告诉我们2个上下文正在尝试将更改注册到同一对象。
由于要及时交付原型,因此我们为整个应用程序创建了一个静态dbContext共享,可以在需要时从任何地方调用它(控制器,模型,逻辑,DataAccess,数据库初始化器)。我们知道这是一个非常肮脏的解决方法,但是比以前的方法效果更好。
仍然存在问题:dbContext一次只能处理1个异步方法调用,并且我们可以有很多调用(例如,userManager.FindByNameAsync-只有异步方法)。例外:“在先前的异步操作完成之前,第二个操作在此上下文上启动”。
我们正在考虑将上下文创建为在控制器中调用动作的第一步,然后将该对象作为“中继竞赛”传递给所调用的所有其他层或函数。这样,连接将从“单击浏览器”开始,直到将响应重新加载到该连接上为止。但是我们不喜欢每个函数都必须有一个额外的参数“上下文”的想法,只是为了共享整个操作路径中各层的连接。
我们确信我们不是第一个想知道使用上下文的正确方法的人。
应用层
我们有这些(逻辑)层,不同的工作空间,但从上到下都是相同的webapp MVC项目:
视图: HTML + Razor + JQuery + CSS。此处的代码仅限于布局,但是某些HTML可能取决于Role。方法调用仅针对控制器,加上utils(如格式设置)。
ViewModels:在控制器和视图之间交换的数据容器。类仅定义属性,以及仅与域实体(译者)之间进行转换的函数。
控制器:从浏览器调用的操作导致对逻辑层中功能的调用。此处的身份验证限制了对操作的访问或操作内部的限制。控制器避免使用Domain实体,而避免使用ViewModels,以便与逻辑层进行通信,从而调用ViewModels转换函数。
域实体:用于逻辑层,并用于由实体框架创建数据库表。
逻辑类:域实体具有包含所有操作的EntityLogic类。这些是所有特定客户客户端通用和抽象的规则(ViewModel未知)的核心。
存储库:用于访问数据库。不确定我们是否确实需要这样做,因为Entity Framework已经将Domain实体映射到数据库中的对象。
典型场景
浏览器在产品控制器中调用操作(POST)以编辑产品。ProductViewModel用作数据的容器。
控制器操作仅限于角色集合。在动作内部,根据角色,将调用另一个Logic函数,并将ProductViewModel转换为ProductDomainEntity并作为参数传递。
逻辑EditProduct函数调用不同逻辑类中的其他函数,并且还使用本地化和安全性进行限制或过滤。逻辑可以调用或不调用存储库来访问数据,或为所有对象使用全局上下文,并将生成的域实体集合传递给逻辑。
根据结果,逻辑可能会或可能不会尝试浏览结果的子级集合。结果作为域实体(或的集合)返回给控制器操作,并且根据此结果,控制器可以调用更多Logic,或者重定向到另一个操作,或者使用将结果转换为正确ViewModel的View进行响应。
在哪里,何时何地以及如何创建dbContext以最佳方式支持整个操作?

更新:逻辑层中的所有类都是静态的。像这样从控制器调用方法:
UserLogic.GetCompanyUserRoles(user)
Run Code Online (Sandbox Code Playgroud)
, 要么
user.GetCompanyRoles()
Run Code Online (Sandbox Code Playgroud)
其中GetCompanyRoles()是在UserLogic中实现的User的扩展方法。因此,没有Logic类的实例意味着没有构造函数接收dbContext以便在其方法中使用。
我想让静态类中的静态方法知道在哪里将dbContext的实例激活到当前HttpRequest。
莫非NInject和OnePerRequestHttpModule帮助,这?有人尝试过吗?
我不相信 EF / DbContexts 的这个问题或任何其他问题有一个“圣杯”或灵丹妙药的答案。因此,我也不相信您的问题有一个明确的答案,并且任何答案都将主要基于意见。然而,我个人发现,在处理 EF 语义和怪癖时,使用 CQRS 模式而不是存储库模式可以实现更多控制并减少问题。以下是一些您可能会(或可能不会)觉得有帮助的链接:
\n\n\n\n\n\nhttps://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91
\n\nhttps://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92
\n\nhttp://github.com/danludwig/tripod
\n\n一些更直接的答案:
\n\n\n\n\n...这使得检索到的对象\xe2\x80\x9c部分可用\xe2\x80\x9d到应用程序的上层(使用技巧.ToList(),受到限制,因为我们可以访问集合和属性,但不能稍后导航到对象子表等)。同样使用来自不同存储库的 2 个上下文,我们得到一个异常,表明 2 个上下文正在尝试注册对同一对象的更改。
\n
这些问题的解决方案是 1) 预先加载最初执行查询时需要的所有子属性和导航属性,而不是延迟加载,2) 每个 HTTP 请求仅使用 1 个 DbContext 实例(控制容器的反转可以帮助解决这个问题)。
\n\n\n\n\n由于交付原型的时间承诺,我们创建了一个为整个应用程序共享的静态 dbContext,需要时可以从任何地方调用它(控制器、模型、逻辑、数据访问、数据库初始值设定项)。我们知道这是一个非常肮脏的解决方法,但它比以前的方法效果更好。
\n
这实际上比“肮脏的解决方法”更糟糕,因为当您拥有 DbContext 实例时,您将开始看到非常奇怪且难以调试的错误static。我很惊讶地听到这比你以前的方法效果更好,但这只是表明如果这个方法效果更好,你以前的方法会存在更多问题。
\n\n\n我们正在考虑将创建上下文作为在控制器中调用操作时的第一步,然后将此对象作为 \xe2\x80\x9crelay race\xe2\x80\x9d 传递到调用的每个其他层或函数。这样,连接将从浏览器中的 \xe2\x80\x9cclick 开始存在,直到响应加载回其上。但我们不喜欢每个函数都必须有一个额外的参数 xe2x80x9ccontextxe2x80x9d 只是为了在整个操作路径中共享各层的连接
\n
这就是控制反转容器可以为您做的事情,这样您就不必继续传递实例。如果您为每个 HTTP 请求注册一个 DbContext 实例,则可以使用容器(和构造函数注入)来获取该实例,而不必在方法参数中传递它(或更糟)。
\n\n\n\n\nViewModels:控制器和视图之间交换的数据容器。类仅定义属性,以及仅与域实体相互转换的函数(转换器)。
\n
小建议:不要在 ViewModel 上声明这样的函数。ViewModel 应该是哑数据容器,没有行为,甚至没有翻译行为。在控制器或其他层(如查询层)中进行转换。ViewModel 可以具有公开基于其他数据属性但没有行为的派生数据属性的函数。
\n\n\n\n\n逻辑类:域实体具有包含所有操作的 EntityLogic 类。这些是核心,其中所有规则都是通用的,并且是从特定消费者客户端抽象出来的(ViewModel 未知)。
\n
这可能是您当前设计中的错误。将所有业务规则和逻辑归结为特定于实体的类可能会变得混乱,尤其是在处理存储库时。跨越实体甚至聚合的业务规则和逻辑又如何呢?它们属于哪个实体逻辑类?
\n\nCQRS 方法使您摆脱这种规则和逻辑的思考模式,更多地进入思考用例的范式。每次“浏览器点击”可能都会归结为用户想要调用或使用的某个用例。您可以找出该用例的参数是什么(例如,要预先加载哪些子/导航数据),然后编写一(一)个查询处理程序或命令处理程序来包装整个用例。当您发现属于多个查询或命令的通用子例程时,您可以将它们分解为扩展方法、内部方法,甚至其他命令和查询处理程序。
\n\n如果您正在寻找一个好的起点,我认为首先学习如何正确使用良好的控制反转容器(如 Ninject 或 SimpleInjector)来注册您的 EF DbContext,这样您将获得最大的收益为每个 HTTP 请求创建 1 个实例。这至少可以帮助您避免处置和多上下文异常。
\n| 归档时间: |
|
| 查看次数: |
1787 次 |
| 最近记录: |