【问题标题】:How to update a single property in Entity Framework Core如何更新 Entity Framework Core 中的单个属性
【发布时间】:2021-11-01 23:17:22
【问题描述】:

我只需要更新一个实体的一两个属性。换句话说,我有一个具有 Id、ParentId、Name 和 Description 的实体。

问题是,当名称更新时,如果数据库中已经存在描述,则会将其清除。

这是代码:

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();;
}

我认为由于我将特定属性设置为已修改,因此只会更新该属性。

或者我应该先从数据库中获取实体,然后再设置属性并保存。我想避免一次保存两次访问数据库。

【问题讨论】:

  • 你的观点是正确的。生成的 SQL 是什么?
  • 你先从DB获取,然后更新
  • @CodeNotFound,我不确定生成的 SQL。我该如何观察呢?
  • @viveknuna,好的,我做到了,是的,它工作得很好。
  • @Greg0 那太好了

标签: c# entity-framework-core


【解决方案1】:

您可以执行以下操作来更新单个属性:

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.ParentId).IsModified = true;
    dbContext.Entry(itemInfoEntity).Property(x => x.Name).IsModified = true;
    dbContext.SaveChanges();
}

但如果你只更新几个属性,那么我认为你不应该将整个对象作为参数发送,只需发送需要更新的属性,如下所示:

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

【讨论】:

  • 以上是我最初尝试的,但发现它擦除了该实体数据库中已经存在的其他数据。
  • 这应该是不可能的,而且我正在使用这种方法而不会清除现有数据。
【解决方案2】:

您真正追求的是并发处理。当多个用户同时编辑同一个对象时,第二个用户将覆盖第一个用户的更改。

或者我应该先从数据库中获取实体,然后设置属性并保存。

是的,但是为对象的每个属性设置一个控制器方法会非常乏味。这就是为什么并发处理是最好的选择。

此外,您的域模型应该与您的数据库模型完全分离。切勿直接在您的 Web 应用程序中使用实体。您已经通过拥有Item 实体(数据库模型)和ItemInfo 类(域模型,用于处理后请求)来这样做。

实现并发处理

首先向您的实体添加一个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; }
}

然后,在您更新实体的地方:

[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);
    }
}

现在,当您尝试从客户端更新项目并收到 409Conflict 状态码时,您应该决定如何处理此问题。我选择在各个输入框下方显示数据库值。

你可以找到这个here的完整实现

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-21
    • 2017-09-17
    • 1970-01-01
    • 2020-01-13
    • 2018-03-21
    相关资源
    最近更新 更多