简体   繁体   English

如何更新 Entity Framework Core 中的单个属性

[英]How to update a single property in Entity Framework Core

I need to update only one or two properties of an Entity.我只需要更新实体的一两个属性。 In other words, I have an entity with an Id, ParentId, Name, and Description.换句话说,我有一个带有 Id、ParentId、Name 和 Description 的实体。

The issue is that when the name is updated, the Description is wiped out if it already existed in the database.问题是当名称更新时,如果描述已经存在于数据库中,它就会被清除。

This is the code:这是代码:

internal void Update(ItemInfo itemInfo)
{
    var item = new Item { Id = itemInfo.Id, ParentId = itemInfo.ParentId, Name = itemInfo.Name };
    var entry = this.DbContext.Items.Attach(item);
    if (item.ParentId != null) entry.Property(x => x.ParentId).IsModified = true;
    if (!(String.IsNullOrWhiteSpace(item.Name))) entry.Property(x => x.Name).IsModified = true;
    this.SaveChanges();;
}

I thought that since I was setting the particular property as modified, only that property would be updated.我认为由于我将特定属性设置为已修改,因此只会更新该属性。

Or should I be getting the entity from the database first, and then just setting the property and saving.或者我应该首先从数据库中获取实体,然后只设置属性并保存。 I wanted to avoid two trips to the database for a single save.我想避免一次保存两次访问数据库。

You can do following in order to update a single property:您可以执行以下操作以更新单个属性:

internal void Update(ItemInfo itemInfo)
{
    if (itemInfo == null) { throw new Exception($"item info was not supplied"); }

    var itemInfoEntity = new ItemInfo()
    {
        Id          = itemInfo.Id,
        ParentId    = itemInfo.ParentId,
        Name        = itemInfo.Name 
    };

    dbContext.ItemInfo.Attach(itemInfoEntity);
    dbContext.Entry(itemInfoEntity).Property(x => x.Id).IsModified = true;
    dbContext.Entry(itemInfoEntity).Property(x => x.Id).IsModified = true;
    dbContext.Entry(itemInfoEntity).Property(x => x.Id).IsModified = true;
    dbContext.SaveChanges();
}

But if you only are updating few properties, then I think you should not send the whole object as parameter, just send the properties that needs to be updated something like this:但是如果你只更新几个属性,那么我认为你不应该将整个对象作为参数发送,只需发送需要更新的属性,如下所示:

internal void Update(id, parentId, name)
{
 ...
}

The thing you're really after is Concurrency Handling.您真正追求的是并发处理。 When multiple users are editting the same object at the same time, the second user will overwrite the changes of the first user.当多个用户同时编辑同一个对象时,第二个用户将覆盖第一个用户的更改。

Or should I be getting the entity from the database first, and then just setting the property and saving.或者我应该首先从数据库中获取实体,然后只设置属性并保存。

Yes, but having a controller method for each property of your object would be very tedious.是的,但是为对象的每个属性都设置一个控制器方法会非常乏味。 That's why concurrency handling is the best option.这就是为什么并发处理是最好的选择。

Also, your domain model should be entirely seperated from your database model.此外,您的域模型应该与您的数据库模型完全分开。 Never use entities in your web application directly.切勿直接在 Web 应用程序中使用实体。 You are already doing so by having the Item entity (database model) and the ItemInfo class (domain model, used to handle the post-request).您已经通过使用Item实体(数据库模型)和ItemInfo类(域模型,用于处理后请求)来实现这一点。

Implement concurrency handling实现并发处理

First add a Timestamp column to your entity:首先向您的实体添加Timestamp列:

internal class Item
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public string Name { get; set; }
    
    [Timestamp]
    [ConcurrencyCheck]
    public byte[] ConcurrencyStamp { get; set; }
}

Then, at the place where you update your entity:然后,在您更新实体的地方:

[Controller]
public class ItemController : Controller
{
    private readonly DbContext dbContext;
    public ItemController(DbContext dbContext)
    {
        this.dbContext = dbContext;
    }
    
    [HttpPost]
    //[Authorize]
    //[ValidateAntiForgeryToken]
    public async Task<ActionResult<ItemInfo>> Update([FromBody] ItemInfo item)
    {
        var existingItem = dbContext.Items.SingleOrDefaultAsync(i => i.Id == item.Id);
        if (Convert.ToBase64String(existingItem.ConcurrencyStamp) != item.ConcurrencyStamp)
        {
            var databaseValue = new ItemInfo
            {
                Id = existingItem.Id,
                ParentId = existingItem.ParentId,
                Name = existingItem.Name,
            };
            return StatusCode(Microsoft.AspNetCore.Http.StatusCodes.Status409Conflict, databaseValue);
        }
        
        // Set new properties
        existingItem.Id = item.Id;
        existingItem.ParentId = item.ParentId;
        existingItem.Name = item.Name;

        // Save changes
        await dbContext.SaveChangesAsync();
        
        // Now return the updated item
        // Now you should map the entity properties back to a new domain model...
        var result = new ItemInfo
        {
            Id = existingItem.Id,
            ParentId = existingItem.ParentId,
            Name = existingItem.Name,
            ConcurrencyStamp = Convert.ToBase64String(existingItem.ConcurrencyStamp),
        };
        return Ok(item);
    }
}

Now when you try to update your item from the client-side, and receive a 409Conflict statuscode, you should decide how you want to handle this.现在,当您尝试从客户端更新您的项目并收到 409Conflict 状态代码时,您应该决定如何处理此问题。 I've chosen to display the database values below the respective input boxes.我选择在相应的输入框下方显示数据库值。

You can find an entire implementation of this here你可以在这里找到完整的实现

您必须首先从数据库中获取实体,然后更新实体。

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

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