简体   繁体   English

为什么 EF Core 不会在我的数据库中更新此项目?

[英]Why won't EF Core update this item on my database?

I'm having trouble updating an item on my database.我在更新数据库中的项目时遇到问题。 I've tried several different ways, but nothing seems to work.我尝试了几种不同的方法,但似乎没有任何效果。 Here is my latest attempt:这是我最近的尝试:

public async Task<Finding>  InsertOrUpdateItemAsync(Item localItem)
{
    using (var context = new AppDbContext())
    {
       context.Items.Update(localItem);
       context.SaveChanges();
    }
}

When SaveChanges executes, I get the following error message: "The instance of entity type ItemStatus cannot be tracked because another instance with the key value ['ItemStatusId: 4'] is already being tracked.执行 SaveChanges 时,我收到以下错误消息:“无法跟踪实体类型 ItemStatus 的实例,因为已在跟踪另一个具有键值 ['ItemStatusId: 4'] 的实例。

Here are the relevant properties of my Item model:以下是我的 Item 模型的相关属性:

public class Item
{   
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ItemId { get; set; }

    public ItemStatus InitialStatus { get; set; }

    public ItemStatus FinalStatus {get; set; }
}

And here are the relevant properties from the ItemStatus class:以下是 ItemStatus 类的相关属性:

public class ItemStatus
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ItemStatusId { get; set; }

    public string ItemStatusName{ get; set; }
]

Can anyone tell me what I'm doing wrong?谁能告诉我我做错了什么? I have checked to make sure I don't have an undisposed context somewhere.我已检查以确保我在某处没有未处理的上下文。 Also, when I look at the change tracker, I can see that it is tracking an entry from each row on the ItemStatus table.此外,当我查看更改跟踪器时,我可以看到它正在跟踪 ItemStatus 表中每一行的条目。 This doesn't seem right.这似乎不对。 Shouldn't it only track the ItemStatus that has been assigned to localItem rather than all the related entities?它不应该只跟踪已分配给 localItem 而不是所有相关实体的 ItemStatus 吗?

I am calling the method from here:我从这里调用方法:

savedItem = awaitdataService.InsertOrUpdateFindingAsync(ItemToDisplay); savedItem = awaitdataService.InsertOrUpdateFindingAsync(ItemToDisplay);

The various properties of ItemToDisplay are bound to dropdown lists in the UI. ItemToDisplay 的各种属性绑定到 UI 中的下拉列表。 I have verified that these properties are being assigned correctly before being sent to the InsertOrUpdateFindingAsync method.在发送到 InsertOrUpdateFindingAsync 方法之前,我已验证这些属性已正确分配。

I have also tried a this for the InsertOrUpdate method:我还为 InsertOrUpdate 方法尝试了这个:

public async Task<Finding>  InsertOrUpdateItemAsync(Item localItem)
{
    using (var context = new AppDbContext())
    {
       Item itemFromDb = context.Items.Where(i => i.ItemId == localItem.ItemId).FirstOrDefault();
       itemsFromDb = localItem;
       context.Items.Update(localItem);
       context.SaveChanges();
    }
}

You apparently save a localItem that refers to two ItemStatus objects both having ItemStatusId = 4. The cause of the error is that these ItemStatus objects are two instances instead of one.您显然保存了一个localItem ,它引用了两个ItemStatus对象的ItemStatusId = 4。错误的原因是这些ItemStatus对象是两个实例而不是一个。 The Update command tries to attach both of these instances to the context. Update命令尝试将这两个实例都附加到上下文中。

There are two ways to fix this:有两种方法可以解决此问题:

  1. Make sure that if both statuses in Item are identical they both refer to the same ItemStatus object.确保如果Item中的两个状态相同,它们都引用相同的ItemStatus对象。 Depending on where localItem comes from, this may require JSON serializer settings or a modification in the code that constructs the localItem object.根据localItem的来源,这可能需要 JSON 序列化程序设置或修改构造localItem对象的代码。

  2. (Preferred) Add the primitive foreign properties InitialStatusId and FinalStatusId to Item and only set these properties, not the references. (首选)将原始外部属性InitialStatusIdFinalStatusId添加到Item并仅设置这些属性,而不是引用。 Then the update is a simple update of scalar properties.那么更新就是对标量属性的简单更新。

Because因为

savedItem = awaitdataService.InsertOrUpdateFindingAsync(ItemToDisplay);

this variable is itemtodisplay, you have to read from once and you keep this data in this variable, so your context to database, tracked this record when you read.这个变量是itemtodisplay,你必须读取一次,你把这个数据保存在这个变量中,所以你的上下文到数据库,当你阅读时跟踪这个记录。

public async Task<Finding>  InsertOrUpdateItemAsync(Item localItem)
{
    using (var context = new AppDbContext())
    {
       context.Items.Update(localItem);
       context.SaveChanges();
    }
}

Then here when you sent this variable to there, there is already tracked variable, you tried to track it one more time.然后在这里,当您将此变量发送到那里时,已经有跟踪变量,您尝试再跟踪一次。

There are 2 solutions:有2个解决方案:

  1. Read this record with .AsNoTracking so it will not be tracked使用.AsNoTracking读取此记录,使其不会被跟踪

  2. When your update method you need to read one more time, like searching with id and keep it in another variable, and assign the parameters which you want to update to sent variable to new read one.当您的更新方法需要再读取一次时,例如使用 id 搜索并将其保存在另一个变量中,然后将要更新的参数分配给已发送的变量给新的读取变量。 then save it so it will not tracked, because there is a new read, actually you read this with tracking parameter because you don't need to add .AsNoTracking here, but it will create a problem because you are trying to change something on same tracked context.然后保存它,这样它就不会被跟踪,因为有一个新的读取,实际上你用跟踪参数读取了这个,因为你不需要在这里添加.AsNoTracking ,但是它会产生一个问题,因为你试图改变同样的东西跟踪的上下文。

I will add basic code based on my 2. desc.我将根据我的 2. desc 添加基本代码。

    public async Task<Finding>  InsertOrUpdateItemAsync(Item localItem)
    {
        using (var context = new AppDbContext())
        {
           var data = context.Items.Where(x => x.Id == localItem.Id).FirstOrDefault();
           data.Name = localItem.Name;
           context.Items.Update(data);
           context.SaveChanges();
        }
    }

I hope I described it well.我希望我描述得很好。

But note: using update method is not the best, because in that example I only want to update name property but it will create query that updates all columns.但请注意:使用 update 方法并不是最好的,因为在该示例中,我只想更新 name 属性,但它会创建更新所有列的查询。 You should do it with out update, else there is 2 option here, if write update, it says hey EF mark all need to update, so it creates a query that to update all columsn.您应该在不更新的情况下执行此操作,否则这里有 2 个选项,如果写入更新,它会说嘿 EF 标记都需要更新,因此它会创建一个查询来更新所有列。 But this data is already tracked, if you change name like example EF Core knows it so you should only write there, SaveChanges();但是这个数据已经被跟踪了,如果你改变名字,比如 EF Core 知道它,所以你应该只写在那里, SaveChanges(); or second method is if you want to update only one record, but your record is read with .AsNoTracking , you should access the context, get entries and change the field as modified或者第二种方法是,如果您只想更新一条记录,但使用.AsNoTracking读取您的记录,您应该访问上下文,获取条目并将字段更改为已修改

public void ContextAttach(TEntity entity)
{
    _dataContext.Attach(entity);
    _dataContext.Entry(entity).Property(Name).IsModified = true;
}

https://blog.oneunicorn.com/2020/01/17/dontcallupdate/ https://blog.oneunicorn.com/2020/01/17/dontcallupdate/

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

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