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



假设模型如下:

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" }          
}    
}

这引发了一个异常:

违反PRIMARY KEY约束"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
}    
}

它会很好用的。然而,由于这是来自外部来源的数据,因此这是不可能的。我的真实场景有多个级别和许多属性。所以我想在模型中解决这个问题。

与其添加实体,不如使用"upstart"库(或创建自己的库(。例如FlexLabs.Upsert就是这样一个实体框架核心库。

在您的情况下,代码将如下所示(基于docu(:

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之前,一位朋友前段时间向我推荐了它,还没有时间对它进行进一步的实验。

最新更新