asp net core -如何验证字段的唯一性

ih99xse1  于 2023-03-31  发布在  .NET
关注(0)|答案(1)|浏览(232)

我在数据库表中有一个唯一的字段。当插入/更新数据时,我可以很容易地验证控制器中数据的唯一性:

if (db.Country.Where(x => x.ID != m.ID && x.Name == m.Name).Count() > 0)
{
   // take action
}

但我认为最好通过使用IValidatableObject接口将此检查合并到模型中:

IEnumerable<ValidationResult> Validate(ValidationContext validationContext);

然而,在这里我没有访问数据库上下文的权限,我需要检查字段的唯一性...?

qhhrdooz

qhhrdooz1#

理想情况下,您的模型不应该知道您的数据库,因为这往往会混淆关注点的分离,并使其更难测试。

public class Country : IValidatableObject
{
    public int ID { get; set; }
    public string Name { get; set; }
    
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var dbContext = (YourDbContext)validationContext.GetService(typeof(YourDbContext));
        if (dbContext.Country.Any(c => c.ID != ID && c.Name == Name))
        {
            yield return new ValidationResult("The name must be unique.", new[] { nameof(Name) });
        }
    }
}

另一个选项是创建一个可以检查唯一性的自定义属性:

public class UniqueCountryNameAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var dbContext = (YourDbContext)validationContext.GetService(typeof(YourDbContext));
        if (dbContext.Country.Any(c => c.ID != ((Country)validationContext.ObjectInstance).ID && c.Name == (string)value))
        {
            return new ValidationResult("The name must be unique.");
        }
        return ValidationResult.Success;
    }
}

public class Country
{
    public int ID { get; set; }
    [UniqueCountryName]
    public string Name { get; set; }
}

正如你在评论中提到的,这个检查也可以在控制器中完成:

[HttpPost]
public IActionResult Create(Country country)
{
    var dbContext = new YourDbContext();
    if (dbContext.Country.Any(c => c.ID != country.ID && c.Name == country.Name))
    {
        ModelState.AddModelError(nameof(country.Name), "The name must be unique.");
    }

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    dbContext.Country.Add(country);
    dbContext.SaveChanges();

    return Ok(country);
}

如果模型上的验证逻辑变得复杂,使用FluentValidation包可能是有意义的。我将其作为一个选项包括在内,因为它是一个流行的包,并且我倾向于用途:

public class CountryValidator : AbstractValidator<Country>
{
    public CountryValidator(YourDbContext dbContext)
    {
        RuleFor(c => c.Name)
            .NotEmpty().WithMessage("The name is required.")
            .MaximumLength(100).WithMessage("The name cannot exceed 100 characters.")
            .Must((country, name) => IsNameUnique(dbContext, country.ID, name))
                .WithMessage("The name must be unique.");
    }

    private bool IsNameUnique(YourDbContext dbContext, int countryId, string name)
    {
        return !dbContext.Country.Any(c => c.ID != countryId && c.Name == name);
    }
}

无论选择哪种选项,都取决于您的用例,但是我不建议使用IValidatableObject的第一个选项,因为自定义ValidationAttribute以更干净的方式实现相同的结果。

相关问题