我不明白为什么到处建议在Seed方法中使用AddOrUpdate?
我们已经开发了半年的应用程序,每次更新服务器时AddOrUpdates都会覆盖用户更改。例如。如果我们打电话给种子:
context.Styles.AddOrUpdate(new Style { Id = 1, Color = "red" });
用户将样式更改为"绿色"然后在下一次服务器更新时,我们将其覆盖为" red"再次,我们得到非常恼火的用户。
看起来如果我们将AddOrUpdate更改为Add,我们将保证覆盖用户数据。如果我们仍然需要一些特殊情况,我们可以将它分开迁移。与常规Configuration.Seed方法不同,特定迁移不会在同一数据库版本上运行两次。
答案 0 :(得分:1)
我认为Style
的主键是Id
。您使用的The overload of AddOrUpdate
仅检查是否有Id == 1
的记录。如果是这样,它会更新它。就是这样。
这里出现的问题是主键是代理键,即它是为了查询方便,但它没有商业意义。通常,对于迁移,您希望查找实体的自然键。这就是用户识别数据的方式。他/她想要一种绿色风格,而不是1
标识的风格。
所以我认为你应该使用this overload of AddOrUpdate
:
context.Styles.AddOrUpdate( s => s.Color,
new Style { Id = 1, Color = "red" });
现在当没有红色样式时,会插入一个新样式,覆盖Id
值(假设它是由数据库生成的)。
从后面的评论中我了解到,您希望在新数据添加时添加数据,但在数据存在时不进行更新(通过主键进行比较)。为此,您可以使用我描述here AddWhenNew
方法的略微调整版本。对于你的情况,我会这样做:
public T void MarkAsAddedWhenNew<T>(this DbContext context,
Expression<Func<T, object>> identifierExpression, T item)
where T : class
{
context.Set<T>().AddOrUpdate(identifierExpression, item);
if (context.Entry(item).State != System.Data.Entity.EntityState.Added)
{
var identifierFunction = identifierExpression.Compile();
item = context.Set<T>()
.Local
.Single(x => identifierFunction(item)
.Equals(identifierFunction(x)));
context.Entry(item).State = System.Data.Entity.EntityState.Unchanged;
}
return item;
}
从本地集合中重新获取该项是一件麻烦事,但由于AddOrUpdate()
中的bug,这是必要的。此错误还会导致将原始条目的状态设置为Unchanged
时出现的错误:它与附加的实例不同。
答案 1 :(得分:0)
Add方法的行为方式具有误导性。它将数据插入到数据库中,即使已经有一行具有与添加相同的PrimaryKey。它只是创建新的PrimaryKey,默默地忽略我们的值。在问这个问题之前我应该尝试过,但无论如何,我认为我并不是唯一一个对此感到困惑的人。所以,在我的情况下,Add甚至比AddOrUpdate更差。
我唯一能解决的问题是:
public static void AddWhenNew<T>(this DbContext ctx, T item) where T : Entity
{
var old = ctx.Set<T>().Find(item.Id);
if (old == null)
ctx.Set<T>().AddOrUpdate(item);
/* Unfortunately this approach throws exception when I try to set state to Unchanged.
Something like:"The entity already exists in the context"
ctx.Set<T>().AddOrUpdate(item);
if (ctx.Entry(item).State != System.Data.Entity.EntityState.Added)
ctx.Entry(item).State = System.Data.Entity.EntityState.Unchanged;
*/
}