简体   繁体   English

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

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

I'm running into a problem with inserting OR updating roughly 950 entities. 我在插入或更新大约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;
}

The code runs in the background of my netcoreapp1.1, with SQLite, and retrieves a list of currencies. 该代码使用SQLite在netcoreapp1.1的后台运行,并检索货币列表。 This is done every 5 minutes with FluentScheduler. 使用FluentScheduler每5分钟完成一次。 Because they're not entirely large objects I do all comparisons in memory, and try to add or update each one. 因为它们不是完全大的对象,所以我会在内存中进行所有比较,并尝试添加或更新每个对象。 My entity has a database-given ID of Id, and the API I'm retrieving from guarantees that CMC_Id is unique. 我的实体具有数据库给定的ID,并且我要从中获取的API保证CMC_Id是唯一的。

The initial insertion works fine. 初始插入工作正常。 I get an error on the second "Update". 我在第二个“更新”时遇到错误。 I believe what's happening is that I'm tracking multiple entities as modified that each have an Id of 0 我相信发生的事情是我正在跟踪多个实体,每个实体的ID为0

I was trying to follow this: https://msdn.microsoft.com/en-us/library/jj592676(v=vs.113).aspx 我正在尝试遵循此方法: https : //msdn.microsoft.com/zh-cn/library/jj592676(v=vs.113).aspx

And the error I get is: "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." 我得到的错误是: "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."

I am unsure how to proceed with updating each row. 我不确定如何继续更新每一行。

Issue here multiple entities with same key are asked to be tracked. 此处发出要求跟踪具有相同密钥的多个实体。

When you set EntityEntry.State to something then EF Core will start tracking the entity in the specific state. 当您将EntityEntry.State设置为某种值时,EF Core将开始跟踪处于特定状态的实体。 Since in your code, you are querying the database to find out existing entity, EF Core will start tracking the entity with given key therefore it throws above exception while setting the EntityEntry.State because there is already entity with same key being tracked. 由于在您的代码中,您正在查询数据库以查找现有实体,因此EF Core将开始使用给定的密钥跟踪该实体,因此在设置EntityEntry.State时会抛出异常,因为已经存在具有相同密钥的实体。

More precisely you are trying to AddOrUpdate . 更准确地说,您正在尝试AddOrUpdate There are multiple ways to achieve the behavior. 有多种方法可以实现该行为。 Which one is the best depends on if you are adding one entity without relation or a complex graph. 哪一个最佳取决于您要添加一个没有关系或复杂图的实体。

The simplest method would be to just check existence instead of tracking the entity from database. 最简单的方法是仅检查存在性,而不是从数据库中跟踪实体。 Options for that would be to use AsNoTracking in your query so that EF does not start tracking it. 可以选择在查询中使用AsNoTracking ,以便EF不会开始对其进行跟踪。 Even more optimized way would be to just get count from database. 甚至更优化的方法是仅从数据库中获取计数。 If you are querying on PK property then count will be either 0 (non-existent) or 1 (existing entity). 如果要查询PK属性,则计数将为0(不存在)或1(现有实体)。 If it does not exist then you call Add otherwise Update . 如果不存在,则调用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();

Since Add or Update methods start tracking whole graph, if your graph is in one consistent state, (all entities are new or all are being modified) then it would work just fine. 由于“ Add或“ Update方法开始跟踪整个图形,因此,如果您的图形处于一种一致的状态(所有实体都是新实体,或者所有实体都正在被修改),那么它就可以正常工作。

If your graph is somewhat inconsistent that state of each node in graph can be different (eg Updating a blog but it has new posts). 如果您的图形有些不一致,则图形中每个节点的状态可能会有所不同(例如,更新博客但其中有新帖子)。 Then you should use EntityEntry.State on individual entity. 然后,您应该在单个实体上使用EntityEntry.State This makes sure that state is applied to only given entity and no other related entity in graph. 这样可以确保状态仅应用于给定的实体,而不会应用于图中的其他相关实体。 Though you need to do above kind of check for each node in the graph. 尽管您需要对图形中的每个节点进行上述检查。 Another alternative is to use Attach method to attach whole graph in Unchanged state and then set state for individual node. 另一种选择是使用Attach方法将整个图AttachUnchanged状态,然后为单个节点设置状态。

If you are having auto-generated Key values then probably you will have PK value set only when it is update else it would be CLR default. 如果您具有自动生成的键值,则可能只有在更新时才设置PK值,否则它将是CLR默认值。 For single entity without relations, you can make that check yourself instead of querying database like above code and make decision. 对于没有关系的单个实体,您可以自己检查而不是像上面的代码那样查询数据库并做出决定。 For graphs, you can use 对于图形,您可以使用

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

This will set state of each node based on PK value being set or not. 这将根据是否设置PK值来设置每个节点的状态。

Hope this helps :) 希望这可以帮助 :)

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

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