哪个更适合数据库播种:Add 或 AddOrUpdate?

Which is better for database seeding: Add or AddOrUpdate?

不明白为什么到处都推荐在Seed方法中使用AddOrUpdate?

我们已经开发了半年的应用程序,每次我们更新服务器时,AddOrUpdates 都会覆盖用户更改。例如。如果我们调用种子:

context.Styles.AddOrUpdate(new Style { Id = 1,  Color = "red"  });

然后用户将样式更改为 "green",然后在下次服务器更新时我们再次将其覆盖为 "red",这让用户非常恼火。

看起来,如果我们将 AddOrUpdate 更改为 Add,我们将保证不会覆盖用户数据。如果我们仍然需要一些特殊情况,我们可以将其单独迁移。与一般的 Configuration.Seed 方法不同,特定的迁移不会 运行 对同一数据库版本进行两次。

我假设 Style 的主键是 Id。您使用的 The overload of AddOrUpdate 仅检查是否有包含 Id == 1 的记录。如果是这样,它会更新它。就这些了。

这里的问题在于主键是一个代理键,即为了方便查询而存在,但没有商业意义。通常,对于迁移,您希望查找实体的 自然键 。这就是用户识别数据的方式。 S/he 想要绿色样式,而不是 1.

标识的样式

所以我认为你应该使用 this overload of AddOrUpdate:

context.Styles.AddOrUpdate( s => s.Color,
                            new Style { Id = 1,  Color = "red"  });

现在,当不再有红色样式时,将插入一个新样式,覆盖 Id 值(假设它是由数据库生成的)。

从您后来的评论中,我了解到您希望在新数据时添加数据,但在数据存在时不更新它们(通过主键进行比较)。为此,您可以使用我描述的 hereAddWhenNew 方法的稍微改编版本。对于你的情况,我会这样做:

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 时出现的错误:它与附加的实例不同。

Add 方法的行为方式具有误导性。即使已经有一行与我们添加的主键相同,它也会将数据插入数据库。它只是创建新的 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;              
        */
    }