1

我正在尝试使用可以在运行时使用AssociatedMetadataTypeTypeDescriptionProvider和更改的属性来实现验证TypeDescriptor.AddProvider

如果它只设置一次,它工作正常,但如果你想在验证更改提供者使用Validator.ValidateObjector Entity Framework (它具有自动实体验证 on SaveChanges它会突然卡在你设置的提供者上,并且无法更改它。

让我们假设我们有这个有两个元数据的类:

public class Person
{
    public class CreateMetadata
    {   
        [Required]
        public string Name { get; set; }
    }
        
    public class UpdateMetadata : CreateMetadata
    {
        [Required]
        public string Password { get; set; }
    }

    public string Name { get; set; }
    public string Password { get; set; }
}

创建人员和元数据实例:

Person person = new Person();

TypeDescriptionProvider createMetaProvider;
TypeDescriptionProvider updateMetaProvider;
            
createMetaProvider = new AssociatedMetadataTypeTypeDescriptionProvider(
    type: typeof(Person), 
    associatedMetadataType: typeof(Person.CreateMetadata));

updateMetaProvider = new AssociatedMetadataTypeTypeDescriptionProvider(
    type: typeof(Person),
    associatedMetadataType: typeof(Person.UpdateMetadata));

让我们首先使用Create metadata 验证人员,然后使用Update

Validate(person, createMetaProvider);
Validate(person, updateMetaProvider);
static void Validate(object obj, TypeDescriptionProvider metadataProvider)
{
    TypeDescriptor.AddProvider(metadataProvider, typeof(Person));

    var context = new ValidationContext(obj);
    var validationResults = new List<ValidationResult>();

    Validator.TryValidateObject(obj, context, validationResults);

    string errors = string
        .Join("\n", validationResults
        .Select(x => x.ErrorMessage));

    Console.WriteLine($"{errors}\n");

    TypeDescriptor.RemoveProvider(metadataProvider, typeof(Person));
}

输出:

The Name field is required.

The Name field is required.

您可能会注意到,它在两种情况下都使用创建元数据。让我们交换它们:

Validate(person, updateMetaProvider);
Validate(person, createMetaProvider);

输出:

The Name field is required.
The Password field is required.

The Name field is required.
The Password field is required.

同样的结果。在这两种情况下都使用第一个提供程序。

正如我之前提到的,保存更改后实体框架也会发生这种情况。

我找到的唯一解决方案是使用自定义验证,但这不能与 EF 一起使用。
尽管可以在 EF 中禁用自动验证,但我认为这更像是 hack 而不是解决方案。

这是我的验证实现:

static void Validate(object obj, TypeDescriptionProvider metadataProvider)
{
    TypeDescriptor.AddProvider(metadataProvider, typeof(Person));

    var sb = new StringBuilder();
    foreach(PropertyDescriptor property in TypeDescriptor.GetProperties(obj))
    {
        foreach(ValidationAttribute attribute in property.Attributes.OfType<ValidationAttribute>())
        {
            if (attribute.IsValid(property.GetValue(obj)))
                continue;

            sb.AppendLine(attribute.FormatErrorMessage(property.Name));
        }
    }
    Console.WriteLine($"{sb}");

    TypeDescriptor.RemoveProvider(metadataProvider, typeof(Person));
}

用法:

Validate(person, updateMetaProvider);
Validate(person, createMetaProvider);

输出:

The Name field is required.

The Name field is required.
The Password field is required.

完整的源代码

更新 1:Richard Deeming 发现验证器在内部缓存属性,这解释了所描述的行为。 验证属性存储

4

0 回答 0