使用LINQ to SQL和每个原子操作一个DataContext的问题

时间:2008-11-03 18:32:19

标签: c# linq-to-sql persistence ddd-repositories

我已经开始在一个(类似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()应该有所帮助。
    • 显然它没有......

有没有办法解决这个痛苦?

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

5 个答案:

答案 0 :(得分:4)

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

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

另请参阅:Multiple/single instance of Linq to SQL DataContextLINQ to SQL - where does your DataContext live?

答案 1 :(得分:1)

您必须:

1)保持上下文打开,因为您还没有完全确定将使用哪些数据(又称Lazy Loading)。

或2)如果您知道自己需要其他财产,请在初始负载上提取更多数据。

对后者的解释:here

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

  

指定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;

答案 4 :(得分:0)

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

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”保留为成员变量,然后在命中之前设置db。