2012-04-18 111 views
1

当您使用存储库模式开发ASP.NET应用程序时,请为每种方法创建一个带有使用块的新实体容器实例(上下文),或者你创建了容器的类级别/私有实例供任何存储库方法使用,直到存储库本身被丢弃为止?除了我在下面注意到的,有什么优点/缺点?有没有一种方法可以结合我没有看到的每种方法的优点?您的存储库是否实现了IDisposable,允许您为repo实例创建块?ASP.NET MVC - 使用实体框架的存储库模式

多个容器(相对于单个)

优点:

  • 被防止连接自动关闭/设置(将在使用块的末尾被关闭)。
  • 有助于强制您将特定视图/视图模型所需的内容拉入内存中,并在较少的往返行程中(对于尝试延迟加载的任何内容,您将获得连接错误)。

缺点:

  • 控制器内的子实体的访问/查看仅限于你叫什么包括()
  • 对于像仪表板指数,显示信息页面许多聚集表(许多不同的库方法调用),我们将增加创建和处理许多实体容器的开销。
+0

关闭主题?真的吗?我可以看到关闭不是建设性的(虽然我也会反对这一点,但肯定比“off topic”更准确) – 2012-04-18 17:51:02

回答

2

如果您实例存储库中的您的上下文,那么你应该总是做在本地,并且在using语句包裹。

如果您使用依赖注入注入的背景下,然后让你的DI容器处理调用dispose上下文当请求完成。

不要直接实例化您的上下文作为类成员,因为这不会在发生垃圾回收之前处理上下文资源。如果你这样做,那么你将需要实现IDipsosable来处理上下文,并确保正在使用您的存储库的任何东西都能正确处理您的存储库。

+0

你只需要自己处置库类(并且将上下文置于repo的Dispose方法中),那么你可以安全地使用该类作为成员的上下文。他在他的问题中已经表明了这一点:“*您的存储库是否实现了IDisposable,允许您为回购实例创建块?*” – Slauma 2012-04-18 18:23:52

+0

@Mystere伟大的回应 - 我很惊讶我从未想过为此目的使用DI尽管它似乎完全适合它。简单的解决方案,谢谢! – Keith 2012-04-18 18:57:57

0

我个人将我的上下文放在我的存储库中的类级别上。我这样做的主要原因是因为存储库模式的一个明显优势是我可以轻松地交换存储库并利用不同的后端。请记住 - 存储库模式的目的是为您提供一个为某些客户端提供备份数据的界面。如果您切换数据源,或者只是想通过依赖注入提供新的数据源,那么如果您在每个方法级别执行此操作,则会创建更难的问题。

微软的MSDN网站有很好的信息repository pattern。希望这有助于澄清一些事情。

+0

不幸的是,这意味着Dispose不会在您的上下文中调用,这意味着您可以打开数据库连接(加上使用中的大量内存),直到垃圾收集器开始清理它。这就是为什么你应该总是将你的上下文封装在一个使用块中,以便尽快处理它们。 – 2012-04-18 17:46:42

+0

@MystereMan - 好点,但你如何解决我在我的文章中提出的另一个问题? (现在你有责任为每一种添加重要的开发/代码开销的方法进行设置。)我个人从来没有见过这样做。 (并不意味着我是正确的,但它使我更加怀疑。) – JasCav 2012-04-18 17:50:31

+0

我不明白你的第二个问题。如果使用依赖注入,则让DI容器处理调用dispose。如果他们使用DI与实例化他们自己的对象,则类的设计方式会有所不同。切换可能更多工作,但这就是为什么你应该从DI开始。 – 2012-04-18 17:54:06

0

我不同意所有四点:

被自动关闭/设置防止连接(将在使用块的结尾关闭 )。

在我看来,如果在方法级别,存储库实例级别或请求级别上处理上下文,则无关紧要。 (你当然在单个请求结束时处理上下文 - 通过将存储库方法包装在using声明中或通过在存储库类上实现IDisposable(如您所建议的)并将存储库实例包装在using语句或通过在控制器构造函数中实例化存储库并将其置于控制器类的重写中 - 或者在请求开始时通过实例化上下文并在请求结束时进行处理(某些依赖注入容器将有助于做这个工作)。)为什么上下文应该“自动处理”?在桌面应用程序中,每个窗口/视图都有一个可能会打开几个小时的上下文是可能的和常见的。

有助于迫使你只能拉入内存,你需要什么样的 特定视图/视图模型,并在更短的往返(你会得到任何你试图延迟加载一个 连接错误)。

老实说,我会通过完全禁用延迟加载来强制执行此操作。我没有看到在客户端与服务器断开连接的Web应用程序中进行延迟加载的好处。在你的控制器动作中,你总是知道你需要加载什么,并且可以使用急切或显式加载。为避免内存开销并提高性能,您可以始终禁用GET请求的更改跟踪,因为EF无论如何都无法跟踪客户端网页上的更改。

控制器内的子实体的访问/查看仅限于什么 你调用包括()

这是相当具有优势不是一个劣势,因为你没有的unwished惊喜延迟加载。如果您需要在控制器动作后填充子实体,这取决于一些条件,你可以通过额外的储存库的方法(LoadNavigationProperty或东西)具有相同或甚至一个新的上下文加载它们。

对于像仪表板指数显示,从 许多表(很多不同的版本库的方法调用)收集的信息的网页,我们将添加 开销创建和处理许多实体容器。

创建上下文 - 我不认为我们正在谈论数百或数千个实例 - 是一种便宜的操作。我认为这是一个非常理论化的开销,在实践中没有发挥作用。

我使用这两种方法,你在web应用中提到,也是第三个选项,即创建每个请求一个上下文并注入同样的情况下到每一个存储库/服务,我需要在一个控制器动作。他们三人都为我工作。

当然,如果你使用,你必须要小心做同一个工作单位的各项工作,以避免安装实体多个上下文,这将导致众所周知的例外多个上下文。避免这种情况通常不是问题,但需要更多的关注,特别是在处理POST请求时。

我最近使用每个请求的上下文,因为它更容易,我只是没有看到有非常狭窄的上下文的好处,我没有理由在整个请求处理中使用多个工作单元。如果我需要多个上下文 - 无论出于何种原因 - 我总是可以创建专门的方法来处理它们自己的上下文,而不是请求的“默认上下文”。

+0

我们在这里讨论的是一个web应用程序,而不是桌面应用程序。桌面应用程序通常具有大量资源,并且可以将环境打开几个小时。 Web应用程序往往需要尽快释放资源,因为您不知道服务器将承受多少负载。 Web应用程序与桌面应用程序有着根本不同的资源管理需求。 – 2012-04-18 17:48:39

+0

@MystereMan:你是说在一个请求被完全处理之前,一个web进程可能释放用在请求中的资源*?我没有说要让一个上下文(或多个上下文)在比单个请求更长的时间内打开。我不明白你的批评意见。 – Slauma 2012-04-18 17:58:08

+0

如果将上下文放在类级别上,则在请求完成时不会处理上下文。即使请求不再使用它,它仍将消耗资源和可能的连接,直到垃圾收集发生。如果你使用DI来注入上下文,那么你的DI容器可以在请求结束时调用dispose,或者如果你直接实例化对象,那么你需要将它包装在using语句中,以便在完成后立即释放资源它。 – 2012-04-18 17:59:47