【问题标题】:Linq to Sql - DataContext design issues and considerations in ASP.NET MVC applicationLinq to Sql - ASP.NET MVC 应用程序中的 DataContext 设计问题和注意事项
【发布时间】:2009-10-17 17:07:26
【问题描述】:

我在 ASP.NET MVC 应用程序中使用 Linq to Sql 时使用“每个原子操作的单个数据上下文”方法。

到目前为止,我一直在使用单例数据上下文,我了解到它存在很多问题,因此我重构了代码以在每个原子操作中使用单个数据上下文。

一个控制器动作的例子现在如下(重构没有改变这一点):

    public ActionResult List()
    {
        List<Request> requests = this.repository.AllRequests();
        return View(requests);
    }

repository 的类型为 IRepository。我想保留这个抽象,以便能够切换到不同的数据层(根据我最近使用 Linq to Sql 的经验,这可能很快就会发生:))

LinqRepository 实现 AllRequests() 方法如下:

    public List<Request> AllRequests()
    {
        using (DataModelDataContext connection = GetContext())
        {
            return connection.Requests.ToList();
        }
    }

(仅供参考,之前 DataContext 实例是 LinqRepository 的一个字段,而 LinqRepository 被保存为单个静态实例)

DataContext 在方法返回之前被释放。

视图代码现在在访问延迟属性时会抛出 ObjectDisposed 异常:

            <%= Html.Encode(request.Branch.Name) %> //throws

我了解到可能不需要处理 DataContext(此处为 When should I dispose of a data context

当我不处理DataContext(删除使用)时,没有ObjectDisposedException。

即:我将方法更改如下:

    public List<Request> AllRequests()
    {
        DataModelDataContext connection = GetContext();
        return connection.Requests.ToList();
    }

但我想知道,在这种情况下不释放 DataContext 实例有什么影响?

我知道我应该在处理 DataContext 之前从实体实例中读取所有数据(包括延迟属性),但我不想引入另一个抽象(另一个 Request 类,将所有属性复制到它上面) .

我的问题:

实体对象是否持有对其父 DataContext 的强引用,从而阻止它被 GC-ed? (我想是的,只是想 100% 确定)

在需要保留数据层抽象时,您能否就使用 Linq to Sql 的推荐方法提供建议? (包括部分属性更新)

是否有使用在 ASP.NET MVC 中实现 Linq to Sql 的存储库抽象的开源项目?

【问题讨论】:

    标签: asp.net-mvc linq-to-sql architecture datacontext


    【解决方案1】:

    首先,让我们考虑一下发生在你身上的事情。

    你是:

    • 打开数据上下文。
    • 取回一些数据。
    • 关闭数据上下文。
    • 尝试读取您从数据上下文中返回的数据。

    该方案旨在发挥作用。但是,为了始终有效,所有数据都需要预先加载。

    默认情况下,LinqToSql 会延迟(也称为延迟)加载它认为合适的东西。在这种情况下,似乎 Request.Branch 或至少 Request.Branch.Name 正在延迟加载。为了正确延迟加载,数据上下文必须仍然存在,在您的场景中,它不再存在。

    这里有一些选项可以让您的场景正常工作:

    选项 1:

    在处理数据上下文之前以某种方式“强制”加载 Request.Branch.Name。 LinqToSql 可以做到这一点,尽管不一定像您希望的那样简单或方便。这应该允许您继续使用“每个原子操作的单个数据上下文”模式,尽管这种方法还有其他缺点。数据上下文支持围绕更改跟踪、缓存和其他场景的重要功能,虽然您希望尽快处理数据上下文,但保持其开放有显着优势,因此您应该对保持数据持开放态度上下文打开的时间比您当前保持打开的时间长。实现选项 1 的一种方法是使用DTOs,正如您在说“另一个请求类”时所暗示的那样,但在这种情况下,这对您来说可能是矫枉过正。

    选项 2:

    当您的用户开始他们的操作时打开数据上下文,并在用户结束他们的操作时释放它。这称为UnitOfWork 模式。这允许延迟加载正常工作,并为您提供更改跟踪和其他不错的 ORM 功能。实施起来可能更具挑战性,但它最有可能在大多数情况下发挥作用。

    Tvanfosson 的答案介于选项 1 和 2 之间,并且有很多中间地带的机会可以解决您的问题。所有的 ORM 都有一个学习曲线,LinqToSql 也不例外。大多数 ORM 都有类似于 LinqToSql 的数据上下文的东西,从 LinqToSql 切换出来并不能免除您必须了解数据上下文如何以及为何如此工作的责任。

    您永远不想在不释放数据上下文的情况下打开它。这是资源泄漏,非常糟糕。

    我很确定 NerdDinner MVC 示例应用程序同时使用了 LinqToSql 和存储库模式,但我不记得它是否为数据上下文管理实现了 UnitOfWork。

    【讨论】:

    • 感谢您的回答。我将工作单元与原子操作混淆了,您对此提供了一些见解。但是,您确定这一点:“您永远不想打开数据上下文而不释放它。这是资源泄漏,非常糟糕。”? Jon Skeet 的这篇帖子正好相反:stackoverflow.com/questions/389822/…“在大多数情况下,你真的不需要处理它们——这是设计使然”
    • 我阅读了 Jon Skeet/Matt Warren 的文字。从 Matt Warren 的文本中可以清楚地看出,如果您不处置数据上下文,您就是在泄漏资源。 Jon Skeet 然后淡化了资源泄漏的重要性。最好不要故意泄漏资源。对不起,我无能为力。
    • DataContext 被设计成轻量级的,并且在很多事情发生时与它的轻量级兄弟一起在一个池中。您不必担心处理 LinqToSql DataContext 并且由于线程和延迟执行问题,最好不要这样做。我使用 StructureMap IoC 容器根据每个请求来管理我的 DataContext
    • cottsak,我不同意不处置 DataContext 是最佳做法。
    • @Michael Maddox:如果不进行链接,通常可以处理 DC,但我不建议这样做,除非您正在调试您认为与连接/线程相关的问题。如果你这样做'只是因为你可以',那么处理 DC 可能会导致多个线程中的竞争条件。所以我建议你在大多数情况下让 DC 自行处理。
    【解决方案2】:

    我的解决方案是让存储库实现 IDisposable。然后我会在 OnActionExecuting 的构造函数中将 Repository 实例化为控制器中的实例变量。然后我会在 OnResultExecuted 中处理存储库。当您到达 OnResultExecuted 时,结果已经被渲染并且所有需要的数据都已经被消耗掉了。存储库将类似地在其构造函数中实例化 DataContext 并在释放时将其释放。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-13
      • 2010-12-01
      • 1970-01-01
      • 2011-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多