C# 验证策略

C# 验证策略,c#,.net,generics,data-annotations,C#,.net,Generics,Data Annotations,我正在尝试构建一系列属性类,以使我们的开发团队更容易验证对象。对象是这样的POCO类 public class User { public string Name { get; set; } public string Company { get; set; } } public class User { public string Name { get; set; } public string Company { get; set; } // Ch

我正在尝试构建一系列属性类,以使我们的开发团队更容易验证对象。对象是这样的POCO类

public class User
{
    public string Name { get; set; }
    public string Company { get; set; }
}
public class User
{
    public string Name { get; set; }
    public string Company { get; set; }

    // Check if an item exists in this list.
    [MustHaveMoreThanOneItem]
    public IList<Client> Clients { get; set; }
}
if(model.Clients.Count > 0) ...
if(model.Name != null) ...
if(model.Clients.GroupBy(x => x.Country == Country.USA).Count >= 1) ...
if(model.Clients.Where(x => x.CompanyName == Company.Google).ToList().Count > 1 ) ...
.
.
.
我想用自定义属性装饰这个模型

public class User
{ 
    [MustHaveValue]
    public string Name { get; set; }
    public string Company { get; set; }
}
然后,我将创建自己的类来实现ValidationAttribute,它是.NET Framework中的基类,属于System.ComponentModel.DataAnnotations

然后,我可以随时验证用户模型,方法是创建一组实例,如ValidationContext、List

但在企业环境中,问题不能由特定的类来解决。我的验证场景需要更复杂、更灵活的方法。假设所需的验证场景之一是这样的

public class User
{
    public string Name { get; set; }
    public string Company { get; set; }
}
public class User
{
    public string Name { get; set; }
    public string Company { get; set; }

    // Check if an item exists in this list.
    [MustHaveMoreThanOneItem]
    public IList<Client> Clients { get; set; }
}
if(model.Clients.Count > 0) ...
if(model.Name != null) ...
if(model.Clients.GroupBy(x => x.Country == Country.USA).Count >= 1) ...
if(model.Clients.Where(x => x.CompanyName == Company.Google).ToList().Count > 1 ) ...
.
.
.
您已经知道它不起作用了,因为它只是针对列表的强类型。所以我决定用泛型来解决这个问题

但令我失望的是,_属性接口不支持泛型。没有像_Attribute:Attribute这样的附加实现,因此,没有ValidationAttribute!!我只是不能在这里使用泛型

我想在这里提出两个问题

如果AtterBute支持泛型,这个问题会得到解决吗? 有没有办法实现泛型属性?为了使用 类成员上的[必须有不止一个项]批注?
您可以像这样检查实现IEnumerable的任何对象:

public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
    public override bool IsValid(object value) 
    {
        // omitted null checking
        var enumerable = value as IEnumerable;
        var enumerator = enumerable.GetEnumerator();
        if (!enumerator.MoveNext())
        {
            return false;
        }

        if (!enumerator.MoveNext())
        {
            return false;
        }

        return true;
    }
}

根据定义,C不支持泛型类型属性,尽管这已被主动请求了很长一段时间:

但是,您仍然可以通过构造函数将类型注入验证属性。然后,您可以使用反射或任何您需要的方法来定义您的自定义验证标准

public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
    public Type EnumerableType { get; }
    public MustHaveMoreThanOneItemAttribute(Type t)
        => this.EnumerableType = typeof(ICollection<>).MakeGenericType(t);
    public override bool IsValid(object value)
    {
        var count = this.EnumerableType.GetProperty("Count").GetValue(value) as int?;
        return (count ?? 0) > 1;
    }
}
现在,您可以使用与目标类似的内容:

public class Department
{ 
    public string Name { get; set; }

    [MustHaveMoreThanOneItem(typeof(Employee))]
    public IList<Employee> { get; set; }
}

只是让您知道已经有一个[Required]属性,所以[MustHaveValueAttribute]可能不是requiredIt’s simple。将属性的值强制转换为IEnumerable。所有集合都实现IEnumerable。然后,您可以简单地检查它是否返回至少两个项目。这里不需要泛型。您还可以使用反射来检查对象是否符合您的要求。在您的特定情况下,转换为IEnumerable就足够了,但是,如果您遇到必须知道类型的情况,请使用属性构造函数可以接受类型参数的事实,这样您就可以为它指定typeofEmployee。c语言规范禁止泛型属性更多的是出于决策,而不是技术限制。欲了解更多信息:仅提供此作为Stuart回答的替代方案。我会使用他的解决方案来避免OP对用例的反射,但我认为如果其他用例可以从运行时的类型需求中获益,这是很有趣的。但是检查列表项的数量对我来说是不够的。检查给定列表中有多少匹配项将需要类似以下模型的条件搜索。Clients.GroupByx=>x.Country==Country.USA.Count>=1,这是本文中指定的。这是一个边缘案例,Microsoft团队认为不值得复杂处理。基本上,您可以只将值类型、类型或枚举传递到属性的构造函数中—无lambda表达式。我想您可以通过创建一个包含一个谓词作为Func的类来绕过这个问题,然后将类型传递给自定义属性构造函数以通过activator实例化它们的属性,但这并不是很适合企业,而且会创建太多的类。看见
public class Department
{ 
    public string Name { get; set; }

    [MustHaveMoreThanOneItem(typeof(Employee))]
    public IList<Employee> { get; set; }
}