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

VS-2019中Blazor PWA项目的CRUD验证及唯一性校验实现难题

解决PWA中Customer名称唯一性校验的问题

看起来你遇到的核心问题是跨项目依赖限制导致SHARED层无法注入SERVER服务,以及客户端校验错误无法显示到validation-summary的问题。我来给你几个贴合你项目结构的实用解决方案:

方案1:把唯一性校验移到SERVER层,通过API返回错误到客户端ModelState

因为SHARED项目不能依赖SERVER(避免循环依赖),所以在SHARED的ValidationAttribute里做数据库校验本来就行不通——Attribute是静态实例化的,没法依赖注入服务,而且SHARED也没有DI容器支持。

正确的思路是把唯一性校验放在SERVER的API层,通过ModelState返回错误,再让客户端把这些错误同步到自己的EditContext.ModelState里,这样validation-summary就能自动显示错误了。

步骤1:SERVER层添加校验逻辑

在你的CustomerController的Create/Update方法里,先检查名称是否重复:

[HttpPost]
public async Task<IActionResult> Create(Customer customer)
{
    // 调用Repository检查名称是否重复(编辑时要排除当前ID)
    bool nameExists = await _customerRepository.IsNameDuplicateAsync(customer.CustomerName, customer.Id);
    if (nameExists)
    {
        // 添加错误到ModelState,指定关联字段和错误信息
        ModelState.AddModelError(nameof(Customer.CustomerName), "该客户名称已存在,请更换");
        return BadRequest(ModelState);
    }

    // 正常创建逻辑
    await _customerRepository.CreateAsync(customer);
    return Ok();
}

// 编辑方法同理
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, Customer customer)
{
    if (id != customer.Id) return BadRequest();
    
    bool nameExists = await _customerRepository.IsNameDuplicateAsync(customer.CustomerName, customer.Id);
    if (nameExists)
    {
        ModelState.AddModelError(nameof(Customer.CustomerName), "该客户名称已存在,请更换");
        return BadRequest(ModelState);
    }

    await _customerRepository.UpdateAsync(customer);
    return Ok();
}

步骤2:客户端处理API返回的错误

在你的CLIENT组件的OnSubmit方法里,调用API后如果收到BadRequest,就把错误信息添加到EditContext.ModelState:

private async Task HandleSubmit()
{
    // 先做客户端静态校验(比如DataAnnotation的必填、长度规则)
    bool isClientValid = EditContext.Validate();
    if (!isClientValid) return;

    // 调用API提交数据
    var response = await Http.PostAsJsonAsync("api/customers", CurrentCustomer);
    if (response.IsSuccessStatusCode)
    {
        // 提交成功:跳转、重置表单等逻辑
        NavigationManager.NavigateTo("/customers");
        return;
    }

    // 处理错误:如果是BadRequest,说明是校验错误
    if (response.StatusCode == HttpStatusCode.BadRequest)
    {
        // 读取API返回的错误字典
        var errors = await response.Content.ReadFromJsonAsync<Dictionary<string, string[]>>();
        if (errors != null)
        {
            // 清空之前的错误,避免叠加
            foreach (var key in EditContext.ModelState.Keys)
            {
                EditContext.ModelState[key].Errors.Clear();
            }

            // 添加API返回的错误到ModelState
            foreach (var errorEntry in errors)
            {
                foreach (var errorMessage in errorEntry.Value)
                {
                    EditContext.ModelState.AddModelError(errorEntry.Key, errorMessage);
                }
            }

            // 通知UI校验状态变化,让validation-summary更新
            EditContext.NotifyValidationStateChanged();
        }
    }
    else
    {
        // 处理其他服务器错误(比如500)
        // 可以显示全局错误提示
    }
}

这样一来,服务器端的唯一性校验错误就会自动显示在你的validation-summary里了。

方案2:客户端即时校验(可选,优化用户体验)

如果想在用户输入时就即时提示名称重复,而不是等到提交后,你可以在CLIENT项目里直接调用API获取现有名称列表,然后添加客户端自定义校验规则:

步骤1:在CLIENT组件里加载现有名称

private List<string> _existingCustomerNames = new();
private Customer CurrentCustomer = new();
private string _originalCustomerName; // 编辑模式下保存原始名称

protected override async Task OnInitializedAsync()
{
    // 从API获取所有已存在的客户名称
    _existingCustomerNames = await Http.GetFromJsonAsync<List<string>>("api/customers/names");
    
    // 如果是编辑模式,保存原始名称
    if (CurrentCustomer.Id != 0)
    {
        _originalCustomerName = CurrentCustomer.CustomerName;
    }
}

步骤2:添加自定义校验规则

protected override void OnInitialized()
{
    // 给EditContext添加自定义校验逻辑
    EditContext.OnValidationRequested += (sender, args) =>
    {
        ValidateCustomerNameUniqueness();
    };

    // 监听字段变化,实时校验
    EditContext.OnFieldChanged += (sender, args) =>
    {
        if (args.FieldIdentifier.FieldName == nameof(Customer.CustomerName))
        {
            ValidateCustomerNameUniqueness();
        }
    };
}

private void ValidateCustomerNameUniqueness()
{
    // 清空之前的该字段错误
    EditContext.ModelState[nameof(Customer.CustomerName)]?.Errors.Clear();

    // 判断名称是否重复:新增时直接检查,编辑时排除当前原始名称
    bool isDuplicate = false;
    if (CurrentCustomer.Id == 0) // 新增模式
    {
        isDuplicate = _existingCustomerNames.Contains(CurrentCustomer.CustomerName, StringComparer.OrdinalIgnoreCase);
    }
    else // 编辑模式
    {
        isDuplicate = _existingCustomerNames.Any(name => 
            name.Equals(CurrentCustomer.CustomerName, StringComparer.OrdinalIgnoreCase) 
            && !name.Equals(_originalCustomerName, StringComparer.OrdinalIgnoreCase));
    }

    if (isDuplicate)
    {
        EditContext.ModelState.AddModelError(nameof(Customer.CustomerName), "该客户名称已存在");
    }

    // 通知UI更新
    EditContext.NotifyValidationStateChanged();
}

为什么SHARED层的Attribute没法注入服务?

你之前尝试在SHARED的ValidationAttribute里注入ICustomerService但始终为null,原因有两个:

  1. 依赖关系限制:SHARED不能依赖SERVER,否则会出现循环依赖(SERVER依赖CLIENT,CLIENT依赖SHARED,SHARED再依赖SERVER),VS会直接报错。
  2. Attribute的生命周期:ValidationAttribute是静态构造的,.NET的DI容器不会自动注入到Attribute实例里,所以即使能引用服务,也没法获取到有效实例。

所以需要访问数据库的动态校验,完全不适合放在SHARED的Attribute里,要么放SERVER层做后端校验,要么放CLIENT层通过API做前端校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 19:38:12