【问题标题】:Entity Framework - UPSERT on unique indexes实体框架 - 唯一索引上的 UPSERT
【发布时间】:2015-01-18 21:25:27
【问题描述】:

我对我的问题进行了一些搜索,但找不到任何真正有用的东西。

所以我的问题/困境是这样的: 我知道 mysql 数据库有一个独特的索引系统,可用于使用以下格式在同一查询中插入/更新: insert into t(a,b,c) values(1,1,1) on duplicate keys update b=values(b),c=values(c); 以及用于通过该索引替换现有记录的替换格式。

老实说,我在 MSSQL 中看到的唯一类似的东西是 merge,但我真的一点也不喜欢它,而且验证插入或更新的查询毕竟不是基于唯一索引...

那么我怎样才能将 mysql 独特的 UPSERT 模拟到实体框架中呢?这是我的主要问题...

我的意思是没有从实体集中获取记录并检查它是否为空以进行可能的插入或更新;

我可以得到它吗?或不?任何提示都可能有用

我看到了this,但没有出现在版本 6 中...

实体示例:

    [Table("boats")]
    public class Boat
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int id { get; set; }
        [MaxLength(15)]
        [Index("IX_ProviderBoat",1,IsUnique=true)]
        public string provider_code { get; set; }
        public string name { get; set; }
        [Index("IX_ProviderBoat", 3, IsUnique = true)]
        [MaxLength(50)]
        public string model { get; set; }
        [Index("IX_ProviderBoat", 2, IsUnique = true)]
        [MaxLength(15)]
        [Key]
        public string boat_code { get; set; }
        public string type { get; set; }
        public int built { get; set; }
        public int length { get; set; }            
    }

所以我想使用 EF 根据我的 IX_ProviderBoat 唯一索引更新/插入

【问题讨论】:

  • 您的数据模型不适合 EF。您的boats 表中是否需要Boat.id 以及三个唯一键?为什么不使用Boat.provider_code 作为您的主键?此外,AddOrUpdate() 在 EF6 中可用。
  • AddOrUpdate 在 EntityFramework6 中可用。可能您必须检查其他重载,例如提到的stackoverflow.com/questions/22287852/…
  • @wahwahwah 1) id 只是一个标识符(不是主键); 2) AddOrUpdate 在我的 6.0.0.0 EF 版本上不可见; 3) provider_code 是主键,也是唯一索引部分组;
  • 好吧——那你为什么有一个“id”列呢?这似乎是多余的,但无论你的..船漂浮什么:)? AddOrUpdate() 方法是 IDBSet 的成员...我会发布答案。
  • @HellBaby - 如果您要包含这样的 ID 列,则模式是将其用作主键作为自然键(唯一索引)的代理项,因此您不必对尽可能多的列进行 FK,因此无需级联即可更新自然键

标签: c# sql entity-framework upsert


【解决方案1】:

AddOrUpdate 方法是 IDBSet 的成员,在 EF6 中可用。

AddOrUpdate 方法不是原子操作,来自多个线程的调用不能保证第二个线程Update 而不是再次Adding - 所以你可以得到重复的记录存储。

这个例子已经过测试并且符合您的期望:

        Boat boat = new Boat // nullable fields omitted for brevity 
        {
            boat_code = "HelloWorld",
            id = 1,
            name = "Fast Boat",
            built = 1,
            length = 100
        };

        using (BoatContext context = new BoatContext()) // or whatever your context is
        {
            context.Set<Boat>().AddOrUpdate(boat); // <-- IDBSet!!!
            context.SaveChanges();
        }

如果我们更改boat_codeAddOrUpdate() 方法将添加一条新记录。如果boat_code 是“HelloWorld”,它将更新现有记录。我相信这就是您正在寻找的...

希望这会有所帮助!

【讨论】:

  • 只是让你知道...这个 upsert 不是线程安全的...正如我前几天在生产中发现的...>_
  • 两个线程,两个上下文,相同的命令,相同的时间。重复插入...很多乐趣!我曾希望 EF 原子地使用MERGE。但它使用选择/插入/更新。
  • 通常,当一个人请求一个 Upsert 时,一个人想要一个原子(并且可能是幂等的)方法。我的问题是两个线程(具有不同的 DbContext)同时使用相同的列表调用 .AddOrUpdate。如果没有正确的索引,我最终会得到重复的条目(如果我有正确的索引,我会有例外)。 SQL Server 支持原子 upserts,使用 MERGE 命令。但 EF 不使用它。
  • 天啊。这与处置无关。我没有重用上下文。你有没有读过 EF 的源代码...我告诉你.AddOrUpdate 不是线程安全的。 entityframework.codeplex.com/SourceControl/latest#src/…
  • .AddOrUpdate 用于Migrations,因此是命名空间。它适用于 DbInitializer。初始化程序是单线程的。问题是MERGE 做了一些完全不同的事情。我的观点是让 OP 小心处理它!
猜你喜欢
  • 2017-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-20
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
相关资源
最近更新 更多