我已经开始在一个(有点类似于 DDD)系统中使用 Linq to SQL,它看起来(过于简化)如下:

public class SomeEntity // Imagine this is a fully mapped linq2sql class.
{
    public Guid SomeEntityId { get; set; }
    public AnotherEntity Relation { get; set; }
}

public class AnotherEntity // Imagine this is a fully mapped linq2sql class.
{
    public Guid AnotherEntityId { get; set; }
}

public interface IRepository<TId, TEntity>
{
    Entity Get(TId id);
}

public class SomeEntityRepository : IRepository<Guid, SomeEntity>
{
    public SomeEntity Get(Guid id)
    {
        SomeEntity someEntity = null;
        using (DataContext context = new DataContext())
        {
            someEntity = (
                from e in context.SomeEntity
                where e.SomeEntityId == id
                select e).SingleOrDefault<SomeEntity>();
        }

        return someEntity;
    }
}

现在,我遇到了问题。当我尝试像这样使用 SomeEntityRepository 时

public static class Program
{
    public static void Main(string[] args)
    {
        IRepository<Guid, SomeEntity> someEntityRepository = new SomeEntityRepository();
        SomeEntity someEntity = someEntityRepository.Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"));
        Console.WriteLine(someEntity.SomeEntityId);
        Console.WriteLine(someEntity.Relation.AnotherEntityId);
    }
 }

一切都运行良好,直到程序到达最后一个 WriteLine,因为它会抛出一个 ObjectDisposedException, ,因为 DataContext 不再存在。

我确实看到了实际问题,但我该如何解决这个问题呢?我想有几种解决方案,但迄今为止我想到的解决方案都不适合我的情况。

  • 摆脱存储库模式并为工作的每个原子部分使用新的 DataContext。
    • 我真的不想这样做。一个原因是我不想让应用程序知道存储库。另一个是我不认为让 linq2sql 的东西 COM 可见会很好。
    • 另外,我认为这样做 context.SubmitChanges() 可能会付出比我预期更多的努力。
  • 指定 DataLoadOptions 以获取相关元素。
    • 由于我希望我的业务逻辑层在某些情况下仅回复某些实体,因此我不知道它们需要使用哪些子属性。
  • 禁用所有属性的延迟加载/延迟加载。
    • 不是一个选择,因为有相当多的表并且它们紧密相连。这可能会导致大量不必要的流量和数据库负载。
  • 互联网上的一些帖子说使用 .Single() 应该有所帮助。
    • 显然它不...

有什么办法可以解决这个痛苦吗?

顺便提一句:我们决定使用 Linq t0 SQL,因为它是一个相对轻量级的 ORM 解决方案,并且包含在 .NET 框架和 Visual Studio 中。如果 .NET 实体框架更适合此模式,则可能可以选择切换到它。(我们的实施还没有那么远。)

有帮助吗?

解决方案

Rick Strahl 在这里有一篇关于 DataContext 生命周期管理的好文章: http://www.west-wind.com/weblog/posts/246222.aspx.

基本上,原子操作方法在理论上很好,但您需要保留 DataContext 以便能够跟踪数据对象中的更改(并获取子对象)。

也可以看看: Linq to SQL DataContext 的多个/单个实例LINQ to SQL - 您的 DataContext 位于哪里?.

其他提示

您必须:

1) 保持上下文打开,因为您还没有完全决定将使用哪些数据(又名延迟加载)。

或 2) 如果您知道需要其他属性,则在初始加载时提取更多数据。

后者的解释: 这里

我不确定如果您使用原子工作单元,您是否必须放弃存储库。我使用两者,尽管我承认放弃了乐观并发检查,因为它们无论如何都不能分层工作(不使用时间戳或其他一些必需的约定)。我最终得到的是一个使用 DataContext 的存储库,并在完成后将其丢弃。

这是一个不相关的 Silverlight 示例的一部分,但前三个部分展示了我如何使用具有一次性 LINQ to SQL 上下文的存储库模式,FWIW: http://www.dimebrain.com/2008/09/linq-wcf-silver.html

指定 DataLoadOptions 以获取相关元素。由于我希望我的业务逻辑层在某些情况下仅回复某些实体,因此我不知道它们需要使用哪些子属性。

如果调用者被授予使用 .Relation 属性所需的耦合,那么调用者也可以指定 DataLoadOptions。

DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Entity>(e => e.Relation);
SomeEntity someEntity = someEntityRepository
  .Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"),
  loadOptions);

//

using (DataContext context = new DataContext())
{
  context.LoadOptions = loadOptions;

这就是我所做的,到目前为止效果非常好。

1) 使 DataContext 成为存储库中的成员变量。是的,这意味着您的存储库现在应该实现 IDisposable 而不是保持打开状态......也许你想避免做一些事情,但我并没有发现它有什么不方便。

2)向您的存储库添加一些方法,如下所示:

public SomeEntityRepository WithSomethingElseTheCallerMightNeed()
{
 dlo.LoadWith<SomeEntity>(se => se.RelatedEntities);
 return this; //so you can do method chaining
}

然后,你的来电者看起来像这样:

SomeEntity someEntity = someEntityRepository.WithSomethingElseTheCallerMightNeed().Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"));

您只需要确保当您的存储库访问数据库时,它使用这些辅助方法中指定的数据加载选项...在我的例子中,“dlo”被保留为成员变量,然后在访问数据库之前设置。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top