繁体   English   中英

使用 Entity Framework Core 更新记录的更好(更清洁)方法

[英]Better(Cleaner) way to Update Record using Entity Framework Core

我已经完成了一些在 SO 上提供的解决方案,就像这样 基本上我有一个User class 有很多属性。

public class Users
{
    [Key]
    public Guid Id { get; set; }
    [MaxLength(200)]
    public string Username { get; set; }
    public bool Enabled { get; set; }
    public string Name { get; set; }
    //...Many more properties
}

UpdateUser()方法来更新用户。 我的问题是该方法很长并且由于正在更新的许多属性而看起来不干净。

public async Task<UpdateUserResponse> UpdateUser(UpdateUserRequest userRequest)
{
    var user = await _userRepository.GetByIdAsync(userRequest.Id);

    if (user == null)
        throw new HttpException(HttpStatusCode.BadRequest, $"User does not exist");
    
    user.EmailAddress = userRequest.EmailAddress;
    user.Enabled = userRequest.Enabled;
    user.Name = userRequest.Name;
    user.SurName = userRequest.SurName;
    //...many more properties which make this method to be long
    
    // I attempted to do something like this which gave the error
    // var usrObj = JsonConvert.DeserializeObject<Users>(JsonConvert.SerializeObject(userRequest));
    // var res = await _userRepository.UpdateAsync(usrObj);
        
    context.Entry(user).State = EntityState.Modified;

    var result = await context.SaveChangesAsync();
    //......

    return response;
}

我尝试将userRequest object 反序列化为User类型,但在UpdateAsync()方法中,Entity Framework Core 抱怨以下消息,这是有道理的:

无法跟踪实体类型“用户”的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例。

我正在寻找一种更清洁的方式来做到这一点。

首先,当使用加载/修改/保存模式时,不要调用

context.Entry(user).State = EntityState.Modified;

或者

context.Update(user);

这些强制将实体的所有属性更新到数据库中,并且适用于当您没有从数据库重新加载(并由上下文更改跟踪器跟踪)实体实例时断开连接的实体场景。

当您拥有它时(与有问题的代码一样),您只需要设置一些属性,然后 EF Core 更改跟踪器将自动确定要在数据库中更新哪些属性(如果所有属性都可能根本不会发出更新命令值与原始值相同)。

话虽如此,具体问题是如何在不编写大量代码的情况下设置感兴趣的目标属性。 EF Core 提供开箱即用的EntityEntry.CurrentValues属性和名为SetValues的方法,只要属性名称和类型匹配,它就会与实体 class 实例一起接受字典或任何 object。 类似于 AutoMapper 默认映射的东西。 但请注意,这仅适用于原始(数据)属性,导航属性需要手动更新。

所以有问题的代码可以简化为

public async Task<UpdateUserResponse> UpdateUser(UpdateUserRequest userRequest)
{
    var user = await _userRepository.GetByIdAsync(userRequest.Id);

    if (user == null)
        throw new HttpException(HttpStatusCode.BadRequest, $"User does not exist");
    
    // Assuming GetByIdAsync method returns tracked entity instances
    // If not, you'd need to modify it or use another one which does that
    // The next call is all you need to update matching properties
    context.Entry(user).CurrentValues.SetValues(userRequest);

    var result = await context.SaveChangesAsync();
    //......

    return response;
}

如果您需要更大的灵活性(具有不匹配的源属性,或者不想应用所有源属性,或者想要处理导航属性等),您最好使用一些 3rd 方库,如 AutoMapper。

您只需要在 where 条件之后使用 AsNoTracking()

例子:

var blogs = context.Blogs.Where(<your condition>)
    .AsNoTracking().
    .ToList();

https://docs.microsoft.com/en-us/ef/core/querying/tracking

https://www.c-sharpcorner.com/UploadFile/ff2f08/entity-framework-and-asnotracking/#:~:text=The%20AsNoTracking()%20extension%20method,are%20returned%20by%20the%20query

暂无
暂无

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

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