哪个更适合数据库种子:添加或AddOrUpdate?

时间:2015-01-13 17:18:55

标签: entity-framework ef-migrations

我不明白为什么到处建议在Seed方法中使用AddOrUpdate?

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

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

用户将样式更改为"绿色"然后在下一次服务器更新时,我们将其覆盖为" red"再次,我们得到非常恼火的用户。

看起来如果我们将AddOrUpdate更改为Add,我们将保证覆盖用户数据。如果我们仍然需要一些特殊情况,我们可以将它分开迁移。与常规Configuration.Seed方法不同,特定迁移不会在同一数据库版本上运行两次。

2 个答案:

答案 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;              
        */
    }