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

Blazor中如何确保ViewModel按正确状态初始化,适配EditForm的新旧实体场景

最优方案选型建议

针对Blazor EditForm同时适配新建、编辑两种场景的ViewModel需求,优先推荐构造函数链方案,兼顾代码复用、可继承性和易用性:

方案1:构造函数链(首选)

利用C#构造函数重载调用的特性,把所有校验逻辑集中到核心的受保护构造函数中,完全避免代码重复,同时支持子类继承:

public class PersonViewModel
{
    // 核心校验逻辑全部集中在这个protected构造函数中
    protected PersonViewModel(Guid? id, string name, int age)
    {
        // Id校验:有值的情况下不能是空Guid
        if (id.HasValue && id.Value == Guid.Empty)
            throw new InvalidOperationException($"{nameof(id)} 是无效值");
        // 公共字段校验
        if (string.IsNullOrWhiteSpace(name))
            throw new InvalidOperationException($"{nameof(name)} 不能为空或空白字符");
        if (age < 0)
            throw new InvalidOperationException($"{nameof(age)} 不能小于0");

        Id = id;
        Name = name;
        Age = age;
    }

    // 新建场景调用:不需要传Id
    public PersonViewModel(string name, int age) : this(null, name, age) { }

    // 编辑场景调用:必须传有效Id
    public PersonViewModel(Guid id, string name, int age) : this(id, name, age) { }

    public Guid? Id { get; }
    // 适配EditForm绑定要求,设置为public set
    public string Name { get; set; }
    public int Age { get; set; }
}

该方案的优势:

  • 无重复校验逻辑,所有规则集中维护,修改时只需要改一处
  • 保留公开构造函数,子类可以正常调用基类构造函数实现扩展
  • 两种创建场景显式区分,调用方不会出现参数传错的问题
  • 可以按需调整属性的访问器,满足Blazor双向绑定的要求

方案2:拆分独立ViewModel(适合场景差异大的情况)

如果新建和编辑场景的字段、校验规则差异较大,或者需要严格避免非法状态(比如编辑场景下Id绝对不能为null),可以拆分两个独立的ViewModel,通过基类复用公共逻辑:

// 基类存放公共字段和公共校验规则
public abstract class PersonViewModelBase
{
    protected PersonViewModelBase(string name, int age)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new InvalidOperationException($"{nameof(name)} 不能为空或空白字符");
        if (age < 0)
            throw new InvalidOperationException($"{nameof(age)} 不能小于0");
        
        Name = name;
        Age = age;
    }

    public string Name { get; set; }
    public int Age { get; set; }
}

// 新建场景专用ViewModel:无Id字段
public class NewPersonViewModel : PersonViewModelBase
{
    public NewPersonViewModel(string name, int age) : base(name, age) { }
}

// 编辑场景专用ViewModel:必填Id
public class ExistingPersonViewModel : PersonViewModelBase
{
    public ExistingPersonViewModel(Guid id, string name, int age) : base(name, age)
    {
        if (id == Guid.Empty)
            throw new InvalidOperationException($"{nameof(id)} 是无效值");
        Id = id;
    }

    public Guid Id { get; set; }
}

该方案的优势是状态完全隔离,从类型层面避免了非法状态的出现,适合表单提交逻辑完全分离的场景,新建接口和更新接口可以分别接收对应的ViewModel参数,安全性更高。

方案3:优化版静态工厂方法

如果你偏好静态工厂方法的创建方式,可以把原本的私有构造函数改为protected,即可解决无法继承的问题:

public class PersonViewModel
{
    // 改为protected,子类可以调用
    protected PersonViewModel() { }

    public Guid? Id { get; protected set; }
    public string Name { get; set; }
    public int Age { get; set; }

    // 新建场景工厂方法
    public static PersonViewModel CreateForNew(string name, int age)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new InvalidOperationException($"{nameof(name)} 不能为空或空白字符");
        if (age < 0)
            throw new InvalidOperationException($"{nameof(age)} 不能小于0");

        return new PersonViewModel
        {
            Name = name,
            Age = age
        };
    }

    // 编辑场景工厂方法
    public static PersonViewModel CreateForEdit(Guid id, string name, int age)
    {
        if (id == Guid.Empty)
            throw new InvalidOperationException($"{nameof(id)} 是无效值");
        var vm = CreateForNew(name, age);
        vm.Id = id;
        return vm;
    }
}

该方案适合创建逻辑比较复杂、需要显式命名区分场景的情况,普通场景下优先级低于构造函数链方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 22:15:08