实体框架Core1.1 - 批量插入或更新 - InvalidOperationException

时间:2017-07-02 17:55:34

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

我遇到插入或更新大约950个实体的问题。

var coins = JsonConvert.DeserializeObject<List<Currency>>(json);
var sw = new Stopwatch();
sw.Start();
using (var ctx = CryptoContext.Get)
{
    var existingCoins = ctx.Coins.ToList();
    foreach (var coin in coins)
    {
        var existing = existingCoins.FirstOrDefault(c => c.CMC_Id == coin.CMC_Id);
        if (existing != null)
        {
            ctx.Entry<Currency>(coin).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
        } else
        {
            ctx.Entry<Currency>(coin).State = Microsoft.EntityFrameworkCore.EntityState.Added;
        }

    }
    ctx.SaveChanges();
    var el = sw.ElapsedMilliseconds;
}

代码在我的netcoreapp1.1的后台运行,使用SQLite,并检索货币列表。使用FluentScheduler每5分钟完成一次。因为它们不是完全大的对象,所以我在内存中进行所有比较,并尝试添加或更新每个对象。我的实体具有数据库给定的ID ID,以及我从保证中检索CMC_Id唯一的API。

初始插入工作正常。我在第二个&#34;更新&#34;上收到错误。我相信发生的事情是我跟踪多个修改过的实体,每个实体的Id为0

我试图遵循这个:https://msdn.microsoft.com/en-us/library/jj592676(v=vs.113).aspx

我得到的错误是:"The instance of entity type 'Currency' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context."

我不确定如何继续更新每一行。

1 个答案:

答案 0 :(得分:0)

此处要求跟踪多个具有相同密钥的实体。

当您将EntityEntry.State设置为某个内容时,EF Core将开始跟踪处于特定状态的实体。由于在您的代码中,您正在查询数据库以查找现有实体,因此EF Core将开始使用给定密钥跟踪实体,因此在设置EntityEntry.State时它会抛出异常,因为已经存在跟踪相同密钥的实体。

更准确地说,您正在尝试AddOrUpdate。有多种方法可以实现这种行为。哪一个是最好的取决于您是否添加一个没有关系的实体或一个复杂的图形。

最简单的方法是检查存在而不是从数据库中跟踪实体。选项可能是在查询中使用AsNoTracking,以便EF不会开始跟踪它。更优化的方法是从数据库中获取数量。如果要查询PK属性,则count将为0(不存在)或1(现有实体)。如果它不存在,那么您拨打Add,否则Update

var updatedBlog = new Blog { Id = 1, Title = "Updated" };
var exist = db.Blogs.Count(b => b.Id == updatedBlog.Id) != 0;
if (exist)
{
    db.Update(updatedBlog);
}
else
{
    db.Add(updatedBlog);
}
db.SaveChanges();

由于AddUpdate方法开始跟踪整个图表,如果您的图表处于一致状态(所有实体都是新的或所有实体都被修改),那么它可以正常工作。

如果您的图表有些不一致,图表中每个节点的状态可能不同(例如,更新博客但有新帖子)。然后,您应该在单个实体上使用EntityEntry.State。这可确保状态仅应用于给定实体,而不应用于图形中的其他相关实体。虽然您需要对图中的每个节点进行上述检查。另一种方法是使用Attach方法将整个图形附加到Unchanged状态,然后为单个节点设置状态。

如果您有自动生成的Key值,那么可能只有在更新时才会设置PK值,否则它将是CLR默认值。对于没有关系的单个实体,您可以自己进行检查,而不是像上面的代码那样查询数据库并做出决定。对于图表,您可以使用

db.ChangeTracker.TrackGraph(updatedBlog, n => n.Entry.State = n.Entry.IsKeySet ? EntityState.Modified : EntityState.Added);

这将根据设置的PK值设置每个节点的状态。

希望这会有所帮助:)