我不同意所有四点:
防止连接被自动关闭/处理(将在 using 块的末尾关闭)。
在我看来,如果您在方法级别、存储库实例级别或请求级别处理上下文并不重要。(当然,您必须在单个请求结束时处理上下文 - 通过将存储库方法包装在using
语句中或通过IDisposable
在存储库类上实现(如您所建议的那样)并将存储库实例包装在using
控制器操作中的语句中或通过在控制器构造函数中实例化存储库并将其处置在Dispose
覆盖控制器类 - 或者通过在请求开始时实例化上下文并在请求结束时处理它(一些依赖注入容器将有助于完成这项工作)。)为什么要“自动处理”上下文?在桌面应用程序中,每个窗口/视图都有一个可能会打开数小时的上下文是可能且常见的。
有助于迫使您仅将特定视图/视图模型所需的内容拉入内存,并且往返次数更少(尝试延迟加载的任何内容都会出现连接错误)。
老实说,我会通过完全禁用延迟加载来强制执行此操作。在客户端与服务器断开连接的 Web 应用程序中,我看不到延迟加载的任何好处。在您的控制器操作中,您始终知道需要加载什么,并且可以使用急切或显式加载。为了避免内存开销和提高性能,您始终可以禁用 GET 请求的更改跟踪,因为 EF 无论如何都无法跟踪客户端网页上的更改。
控制器/视图中子实体的访问仅限于您使用 Include() 调用的内容
这是一个优势而不是劣势,因为您没有延迟加载带来的意外惊喜。如果您需要稍后在控制器操作中填充子实体,根据某些条件,您可以通过LoadNavigationProperty
具有相同甚至新上下文的其他存储库方法(或其他东西)加载它们。
对于显示从许多表(许多不同的存储库方法调用)收集的信息的仪表板索引之类的页面,我们将增加创建和处置许多实体容器的开销。
创建上下文——我不认为我们在谈论成百上千的实例——是一种廉价的操作。我认为这是一种非常理论上的开销,在实践中没有发挥作用。
我已经使用了您在 Web 应用程序中提到的两种方法以及第三种选择,即为每个请求创建一个上下文,并将这个相同的上下文注入到控制器操作中我需要的每个存储库/服务中。他们三个都为我工作。
当然,如果您使用多个上下文,则必须小心在同一个工作单元中完成所有工作,以避免将实体附加到多个上下文,这将导致众所周知的异常。避免这种情况通常不是问题,但需要多加注意,尤其是在处理 POST 请求时。
我最近对每个请求使用上下文,因为它更容易,而且我只是看不到使用非常狭窄的上下文的好处,而且我认为没有理由在整个请求处理中使用多个工作单元。如果我需要多个上下文 - 无论出于何种原因 - 我总是可以创建专门的方法来使用它们自己的上下文而不是请求的“默认上下文”。