You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用EF Core更新对象:非空属性才更新的替代实现方案咨询

灵活更新实体属性的实现方案探讨

我正在开发UpdateEntityCommand,需要实现这样的更新逻辑:当UpdateDrinkDto中的属性为null时,不修改对应Drink实体的属性值,仅更新非空的属性。目前我通过反射实现了这个功能,但不确定这种方式是否合理,想咨询有没有其他更优的实现方式。


相关代码

Drink实体

public class Drink : Product
{
    public string Name { get; set; }
    public int Id { get; set; }
    public double Price { get; set; }
    public string? Explanation { get; set; }
    public double? Calories { get; set; }
    public bool HasStock { get; set; } = true;
    public int? Volume { get; set; }
    public int DrinkCategoryId { get; set; }
    public virtual DrinkCategory DrinkCategory { get; set; }
}

UpdateDrinkDto

public class UpdateDrinkDto
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public double? Price { get; set; }
    public bool? HasStock { get; set; } = true;
    public string? Explanation { get; set; }
    public int? Volume { get; set; }
}

当前反射实现的Handle方法

public async Task<DrinkDto> Handle(UpdateDrinkCommand request, CancellationToken cancellationToken)
{
    Drink? drink = await _drinkRepository.GetAsync(x => x.Id == request.UpdateDrinkDto.Id, x => x.Include(x=>x.DrinkCategory).ThenInclude(x=>x.Menu));
    _drinkBusinessRules.DoesDrinkExists(drink);
    await _businessRules.IsOwnerResponsibleForRestaurant(request.OwnerId, drink.DrinkCategory.Menu.RestaurantId);

    SetProperties(request.UpdateDrinkDto, drink);

    Drink updatedDrink = await _drinkRepository.UpdateAsync(drink);
    DrinkDto response = _mapper.Map<DrinkDto>(updatedDrink);

    return response;
}

private void SetProperties(UpdateDrinkDto updateDrinkDto, Drink drink)
{
    PropertyInfo[] properties = typeof(UpdateDrinkDto).GetProperties();
    foreach (PropertyInfo property in properties)
    {
        var value = updateDrinkDto.GetType().GetProperty(property.Name).GetValue(updateDrinkDto);
        if (value != null)
        {
            typeof(Drink).GetProperty(property.Name)?.SetValue(drink, value);
        }
    }
}

反射实现的优缺点

优点

  • 灵活性高:新增属性时无需修改映射逻辑,自动适配
  • 代码简洁:不用逐个编写属性判断逻辑

缺点

  • 性能损耗:反射操作比直接调用属性慢,高频场景下影响明显
  • 类型不安全:编译时无法检查属性名是否匹配、类型是否兼容,容易出现运行时错误
  • 调试困难:出现问题时难以定位具体是哪个属性映射出错

替代实现方案

1. 手动属性映射(类型安全,性能最优)

直接逐个判断DTO的属性值,非空则更新实体。虽然代码量多,但完全可控,编译时就能发现错误。

private void UpdateDrinkProperties(UpdateDrinkDto dto, Drink drink)
{
    if (dto.Name != null) drink.Name = dto.Name;
    if (dto.Price != null) drink.Price = dto.Price.Value;
    if (dto.HasStock != null) drink.HasStock = dto.HasStock.Value;
    if (dto.Explanation != null) drink.Explanation = dto.Explanation;
    if (dto.Volume != null) drink.Volume = dto.Volume.Value;
}

2. AutoMapper条件映射

配置AutoMapper时,添加条件规则,仅当源属性非空时才进行映射。

首先在AutoMapper配置文件中添加:

CreateMap<UpdateDrinkDto, Drink>()
    .ForAllMembers(opt => opt.Condition((src, dest, srcVal) => srcVal != null));

然后在Handle方法中直接使用Mapper:

// 替换原来的SetProperties调用
_mapper.Map(request.UpdateDrinkDto, drink);

3. 表达式树封装通用方法

表达式树比反射性能好,且能保持类型安全,可以封装成通用工具类,复用不同实体和DTO的更新逻辑。

示例通用方法:

public static void UpdateNonNullProperties<TSource, TTarget>(TSource source, TTarget target)
    where TSource : class
    where TTarget : class
{
    var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
    var targetProperties = typeof(TTarget).GetProperties().Where(p => p.CanWrite);

    foreach (var sourceProp in sourceProperties)
    {
        var targetProp = targetProperties.FirstOrDefault(p => p.Name == sourceProp.Name && p.PropertyType == sourceProp.PropertyType);
        if (targetProp == null) continue;

        var value = sourceProp.GetValue(source);
        if (value != null)
        {
            targetProp.SetValue(target, value);
        }
    }
}

调用时直接用:

UpdateNonNullProperties(request.UpdateDrinkDto, drink);

4. 使用Mapster库

Mapster是一个轻量的对象映射库,支持条件映射,配置简单且性能接近手动映射。

安装Mapster后,配置映射规则:

TypeAdapterConfig<UpdateDrinkDto, Drink>.NewConfig()
    .IgnoreNullValues(true);

然后在Handle方法中使用:

request.UpdateDrinkDto.Adapt(drink);

内容的提问来源于stack exchange,提问作者ashwor11

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 19:53:14