【问题标题】:Linq to sql add/update in different methods with different datacontextsLinq to sql 以不同的方法使用不同的数据上下文添加/更新
【发布时间】:2011-01-29 05:59:22
【问题描述】:

我必须创建数据上下文并返回创建/更新的对象的 Add() 和 Update() 方法。

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

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

我了解该问题,但想知道如何处理?我已经阅读了一些有关如何处理多个数据上下文对象的内容,并且从我所听到的这种方式来看是可以的。

我知道实体仍然附加到 Add() 中的数据上下文,但我需要了解如何解决这个问题?

提前致谢

【问题讨论】:

    标签: c# linq-to-sql datacontext


    【解决方案1】:

    坦率地说,从设计的角度来看,在 AddUpdate 方法中旋转 DataContext 实例是错误的。

    可能这些方法位于某种 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 to SQL 处理分离的实体,但幸运的是让它处理已经的实体附加到一个不同的上下文。即使在分离的情况下,这也确实很麻烦,您需要在所有表/实体上都有时间戳字段,或者开始弄乱版本检查/自动同步属性 - IMO 不值得,只需设计您的存储库以使用 每个实例一个上下文,并在彼此之间共享上下文实例。

    【讨论】:

    • 我开始思考这个问题,从设计的角度来看,在业务逻辑层中开始混合 MSSQL 特定对象(如 datacontext)真的可以吗?
    • @Oskar:这没有混合任何东西;存储库的整个想法是您正在抽象数据库,因此您可以将其作为IFooRepository 传递并让您的域逻辑依赖于此。在我的示例中,我展示了数据上下文和存储库的手动构建,但在生产中,在一个非平凡的系统中,您可能会注入这些依赖项,并且除了 IFooRepository 抽象之外,您还会有一个 @ 987654337@ 或包装context.SubmitChanges 方法的类似实现。我经常在支持多个数据提供者的应用中这样做。
    • 当然,在一次性应用程序中,或者如果您知道您永远不会从 SQL Server 或 Linq 迁移到 SQL,那么我不会浪费时间去 DI-ing 数据库 -相关的东西。只有您自己知道项目的规模、范围和波动性。
    • 这是 oldFoo.Bar = foo.Bar; oldFoo.Baz = foo.Baz; 这样做太糟糕了。 stackoverflow.com/questions/601080/…
    • 抱歉没有回复。在阅读更多这一切意味着什么之后,你现在所说的一切都变得有意义了!如果您能查看我的其他问题stackoverflow.com/questions/5557914/…,我将不胜感激
    【解决方案2】:

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

    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);
            }
        }
    

    【讨论】:

    • 我得到一个异常:System.Runtime.Serialization.SerializationException:类型“xxx”的对象图包含循环,如果禁用引用跟踪,则无法序列化。xxx 是属性的类型被序列化的对象
    • 无论如何,在数据上下文之间重用实体是不可取的。实体对象具有内置的更改跟踪,如果您将其移动到新的数据上下文,则不会更新。您可以创建一个新的实体对象,然后执行 Attach(),因为新对象没有更改跟踪足迹。
    【解决方案3】:

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

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

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

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

    【讨论】:

    • 我确实在抛出异常的方法中有这段代码:Article article = db.Articles.First(a => a.ID == articleID);还是谢谢!
    【解决方案4】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多