Linq to sql使用不同的datacontexts以不同的方法添加/更新

时间:2010-03-18 22:32:52

标签: c# linq-to-sql datacontext

我必须使用方法,Add()和Update(),它们都创建一个datacontext并返回创建/更新的对象。

在我的单元测试中,我先调用Add(),做一些东西,然后调用Update()。问题是Update()失败并出现异常:

  System.Data.Linq.DuplicateKeyException: Cannot add an entity with a key that is already in use..

我理解这个问题,但想知道该怎么办?我已经阅读了一些关于如何处理多个datacontext对象的内容,并且从我听到过这种方式是可以的。

我知道实体仍然附加到Add()中的datacontext但是我需要找出如何解决这个问题?

提前致谢

4 个答案:

答案 0 :(得分:5)

坦率地说,从设计的角度来看,在DataContextAdd方法中提升Update个实例是错误的。

据推测,这些方法属于某种Repository类 - 在这种情况下,应该使用预先存在的DataContext初始化存储库,通常通过构造函数传入。

如果您以这种方式设计存储库,则不必担心此问题:

public class FooRepository
{
    private MyDataContext context;

    public FooRepository(MyDataContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        this.context = context;
    }

    public void Add(Foo foo)
    {
        context.FooTable.InsertOnSubmit(foo);
    }

    public void Update(Foo foo)
    {
        Foo oldFoo = context.FooTable.Single(f => f.ID == foo.ID);
        oldFoo.Bar = foo.Bar;
        oldFoo.Baz = foo.Baz;
    }
}

然后,您从那里执行更新:

Foo fooToSave = GetFooFromWherever();
using (MyDataContext context = new MyDataContext(...))
{
    FooRepository repository = new FooRepository(context);
    repository.Save(fooToSave);
    context.SubmitChanges();
} // Done

可以使用和重用此模式,您可以将多个存储库组合到一个“事务”中;你永远不会遇到任何问题。这就是封装工作单元模式的DataContext实际意图使用的方式。

顺便说一句,在设计存储库时,通常会尝试抽象出繁琐的Insert / Update语义并公开Save方法:

public void Save(Foo foo)
{
    if (foo.ID == default(int))
    {
        Insert(foo);
    }
    else
    {
        Update(foo);
    }
}

这样您就不必担心自己是否已插入Foo

可能强迫Linq与SQL一起处理分离的实体,但是很幸运能够让它处理已经的实体附加到不同的上下文。即使在分离的情况下,它真的很麻烦,你需要在你的所有表/实体上都有时间戳字段或开始搞乱版本检查/自动同步属性 - 它不值得IMO,只需设计你的存储库来使用每个实例一个上下文,并在彼此之间共享上下文实例。

答案 1 :(得分:0)

为了更新附加到另一个数据上下文的实体,首先需要将其从上下文中分离,然后将其附加到其他上下文。分离对象的一种方法如下:

object ICloneable.Clone()
    {
        var serializer = new DataContractSerializer(GetType());
        using (var ms = new System.IO.MemoryStream())
        {
            serializer.WriteObject(ms, this);
            ms.Position = 0;
            return serializer.ReadObject(ms);
        }
    }

答案 2 :(得分:0)

您必须在更新上下文的表实例上为Attach()返回的实体调用Add(),例如updateContext.Products.Attach(product)

如果附加成功,那么您可以更新实体,LINQ to SQL将“知道”执行更新而不是插入。

请记住,尝试附加实体可能会引发各种问题,尤其是在尝试附加对象图而不仅仅是单个对象时。

我只是使用SingleOrDefault()或类似的方法将新实体从数据库加载到更新上下文中,然后对其进行处理,为您提供相同的功能,但更少的错误。

答案 3 :(得分:0)

只有不重复使用该对象才可以。一旦实体实例对象与数据上下文相关联,就添加有关该数据上下文的信息以用于更改跟踪目的。如果您尝试在不同的数据上下文中重用该对象,则会收到错误。

如果您正在使用不同的数据上下文,建议创建一个新的实体对象并在该第一个数据上下文中执行添加,然后使用其他数据上下文检索该记录并使用新的实体对象执行更新

原因是如果您只使用单个实体对象和多个数据上下文,则无法保证信息不会在操作之间发生变化。每个数据上下文都会根据创建实体对象时数据库对象的状态来跟踪和管理更改。