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

在Blazor应用中如何通过MVVM与EF Core仅更新已变更字段?

解决方案

方案1:复用Blazor原生EditContext变更跟踪(首推)

Blazor内置的EditContext已经自带了表单字段变更跟踪能力,不需要自行实现VM的变更跟踪逻辑,完全符合你的需求:

  • 无需修改现有VM结构
  • 自动记录所有用户修改过的字段
  • 仅更新修改字段,不会覆盖数据库中其他字段的最新变更
  • 通用逻辑可以封装为扩展方法,完全符合DRY原则

实现步骤

  1. 编写通用的属性赋值扩展方法,全局复用:
public static class EntityUpdateExtensions
{
    /// <summary>
    /// 仅将VM中修改过的属性赋值到实体
    /// </summary>
    public static void ApplyModifiedProperties<TEntity, TVM>(this TEntity entity, TVM viewModel, IEnumerable<string> modifiedProperties)
        where TEntity : class
        where TVM : class
    {
        var entityType = typeof(TEntity);
        var vmType = typeof(TVM);
        foreach (var propName in modifiedProperties)
        {
            var vmProperty = vmType.GetProperty(propName);
            var entityProperty = entityType.GetProperty(propName);
            // 跳过不存在/不可写的属性
            if (vmProperty == null || entityProperty == null || !entityProperty.CanWrite) 
                continue;
            var value = vmProperty.GetValue(viewModel);
            entityProperty.SetValue(entity, value);
        }
    }
}
  1. 页面中使用EditContext绑定VM:
<EditForm EditContext="_editContext" OnValidSubmit="HandleSave">
    <InputText @bind-Value="ViewModel.Name" />
    <InputNumber @bind-Value="ViewModel.Price" />
    <!-- 其他表单控件 -->
    <button type="submit">保存</button>
</EditForm>

@code {
    private ViewModel ViewModel { get; set; }
    private EditContext _editContext;

    protected override async Task OnInitializedAsync()
    {
        // 原有加载VM的逻辑不变
        using var dbContext = DbContextFactory.CreateDbContext();
        var entity = await dbContext.Products.FindAsync(ProductId);
        ViewModel = new ViewModel { Id = entity.Id, Name = entity.Name, Price = entity.Price };
        
        // 初始化EditContext,自动跟踪变更
        _editContext = new EditContext(ViewModel);
    }

    private async Task HandleSave()
    {
        // 无变更直接跳过保存
        if (!_editContext.IsModified()) return;

        using var dbContext = DbContextFactory.CreateDbContext();
        // 加载数据库最新实体
        var entity = await dbContext.Products.FindAsync(ViewModel.Id);
        // 仅赋值修改过的字段
        entity.ApplyModifiedProperties(ViewModel, _editContext.GetModifiedProperties());
        await dbContext.SaveChangesAsync();

        // 重置变更状态,方便后续继续编辑
        _editContext.MarkAsUnmodified();
    }
}

复杂层级场景适配

如果存在嵌套VM、子集合这类层级结构,只需要引入支持对象图跟踪的EditContext扩展,不需要修改核心逻辑:

  • 内置的ObjectGraphDataAnnotationsValidator已经支持嵌套对象的字段变更跟踪
  • 子集合的变更跟踪可以通过封装可追踪集合组件实现,核心的属性赋值逻辑无需修改

方案2:可追踪VM基类(适用于非表单编辑场景)

如果你的编辑逻辑不在EditForm中,可以封装通用的可追踪VM基类,自动收集变更字段:

public abstract class TrackableVM : INotifyPropertyChanged
{
    private readonly HashSet<string> _modifiedProperties = new();

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        _modifiedProperties.Add(propertyName);
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public IReadOnlySet<string> GetModifiedProperties() => _modifiedProperties.ToHashSet();

    public void MarkAsUnmodified() => _modifiedProperties.Clear();

    // 封装属性赋值的快捷方法,减少VM样板代码
    protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

业务VM继承该基类后,属性使用SetField赋值即可自动跟踪变更,保存时和方案1一样调用扩展方法赋值即可。

进阶:并发冲突防护

如果需要避免用户修改的字段刚好被其他人覆盖,可以在实体中添加并发令牌:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    // 并发令牌,EF Core自动管理
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

保存时捕获DbUpdateConcurrencyException,提示用户记录已被他人修改,需要刷新后重试即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 19:06:06