实体框架(EF6)插入或更新(如果存在)属性

时间:2018-11-15 15:05:57

标签: c# entity-framework entity-framework-6

假设模型如下:

class Foo {
    virtual Bar Bar {get; set ;}
}

class Bar {
    int Id { get; set; }
    string Property {get; set;}
}

class MyContext {
     virtual DbSet<Foo> Foos {get; set;}

    void OnModelCreating(DbModelBuilder modelBuilder)
    {
         modelBuilder.Entity<Bar>()
            .HasKey(c => c.Property);
    }
}

还有一些类似的代码:

void DoStuff() {
   var foos = GetFoosFromExternalSource();

   using(var ctx = new MyContext() {
       foreach(var foo in foos) {
            ctx.Foos.Add(foo);
       }

       ctx.SaveChanges();
   }
}

IEnumerable<Foo> GetFoosFromExternalSource() {
     yield return new Foo {
          Bar = new Bar { Id = 1, Property = "Hello" }          
     };
     yield return new Foo {
          Bar = new Bar { Id = 2, Property = "World" }          
     };
     yield return new Foo {
          Bar = new Bar { Id = 1, Property = "Hello" }          
     }    
}

这将引发异常:

  

违反主键约束'PK_dbo.Bar'。无法在对象“ dbo.Bar”中插入重复键。重复的键值为(Hello)。

如何让EF清楚,如果Bar对象具有相同的Key(或Id,或两者),则它被视为同一实例?

我知道,如果我可以做类似的事情

 IEnumerable<Foo> GetFoosFromExternalSource() {
     var bar1 = new Bar { Id = 1, Property = "Hello" };
     var bar2 = new Bar { Id = 2, Property = "World" };

     yield return new Foo {
          Bar = bar1          
     };
     yield return new Foo {
          Bar = bar2         
     };
     yield return new Foo {
          Bar = bar1
     }    
}

它将正常工作。但是,由于这是来自外部源的数据,因此这是不可能直接实现的。我的真实场景具有多个级别和许多属性。所以我想在模型中解决这个问题。

1 个答案:

答案 0 :(得分:1)

您应该使用“ upsert”库(或创建自己的库)来代替添加实体。例如FlexLabs.Upsert就是这样的Entity Framework Core库。

根据您的情况,代码如下所示(基于文档):

async Task DoStuff()
{
   var foos = GetFoosFromExternalSource();
   using(var ctx = new MyContext()
   {
       await ctx.Foos
           .UpsertRange(foos)
           .On(f => f.Property)
           .RunAsync();
       ctx.SaveChanges(); // not sure if savechanges call is necessary based on docu...
   }
}

注意:我以前没有使用过FlexLabs.Upsert,一段时间前一位朋友向我推荐了它,还没有时间进一步尝试。