繁体   English   中英

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

[英]Entity Framework Core1.1 - Bulk Insert or Update - InvalidOperationException

我在插入或更新大约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;
}

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

初始插入工作正常。 我在第二个“更新”时遇到错误。 我相信发生的事情是我正在跟踪多个实体,每个实体的ID为0

我正在尝试遵循此方法: https : //msdn.microsoft.com/zh-cn/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 (ie 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."

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

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

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

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

最简单的方法是仅检查存在性,而不是从数据库中跟踪实体。 可以选择在查询中使用AsNoTracking ,以便EF不会开始对其进行跟踪。 甚至更优化的方法是仅从数据库中获取计数。 如果要查询PK属性,则计数将为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();

由于“ Add或“ Update方法开始跟踪整个图形,因此,如果您的图形处于一种一致的状态(所有实体都是新实体,或者所有实体都正在被修改),那么它就可以正常工作。

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

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

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

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

希望这可以帮助 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM