动态RegularExpression属性

时间:2013-08-20 16:43:52

标签: c# asp.net-mvc

我有这个表格,其中有一个邮政编码字段,在我的ViewModel中它看起来像这样:

[RegularExpression(@"^\d{5}(-\d{4})?$")]
public string PostalCode { get; set; }

该正则表达式接受5位数的邮政编码,但现在我需要支持使用8位,4位或6位邮政编码的其他国家/地区。

我在数据库中有这些自定义正则表达式,但我不能以这种方式将非静态变量传递给属性:

[RegularExpression(MyCustomRegex)]
public string PostalCode { get; set; }

我该怎么办?我尝试创建自定义属性,但在某些时候我需要传递一个非静态参数,这是不可能的。

我应该使用反射吗?有更清洁的方式吗?

2 个答案:

答案 0 :(得分:1)

更好的方法是将属性与正则表达式分离。

public class PostalCodeAttribute : Attribute
{
    public string Country { get; set; }
}

public interface IPostalCodeModel
{
    string PostalCode { get; }
}

public class UsModel : IPostalCodeModel
{
    [PostalCode(Country = "en-US")]
    public string PostalCode { get; set; }
}

public class GbModel : IPostalCodeModel
{
    [PostalCode(Country = "en-GB")]
    public string PostalCode { get; set; }
}

验证

public class PostalCodeValidator
{
    private readonly IRegularExpressionService _regularExpressionService;

    public PostalCodeValidator(IRegularExpressionService regularExpressionService)
    {
        _regularExpressionService = regularExpressionService;
    }

    public bool IsValid(IPostalCodeModel model)
    {
        var postalCodeProperty = model.GetType().GetProperty("PostalCode");

        var attribute = postalCodeProperty.GetCustomAttribute(typeof(PostalCodeAttribute)) as PostalCodeAttribute;

        // Model doesn't implement PostalCodeAttribute
        if(attribute == null) return true;

        return ValidatePostalCode(_regularExpressionService, model, attribute.Country);
    }

    private static bool ValidatePostalCode(
        IRegularExpressionService regularExpressionService,
        IPostalCodeModel model,
        string country
    )
    {
        var regex = regularExpressionService.GetPostalCodeRegex(country);
        return Regex.IsMatch(model.PostalCode, regex);
    }
}

答案 1 :(得分:0)

如几个相关问题所示(例如Pass instance of Class as parameter to Attribute constructor Lambda expression in attribute constructor),只允许编译时间文字作为属性的参数。

我确实想到了可能会或可能不会起作用的解决方法。我们的想法是创建一个自定义属性类,该类派生自正则表达式属性,并在构造上执行正则表达式查找,并将结果传递给它的基础。

免责声明:我实际上没有测试过(我不打算这样做; - )。

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class PostalCodeAttribute : RegularExpressionAttribute
{
    private static ConcurrentDictionary<string, Func<string, string>> _resolverDict = new ConcurrentDictionary<string, Func<string, string>>();
    private static string Resolve(string source)
    {
        Func<string, string> resolver = null;
        if (!_resolverDict.TryGetValue(source, out resolver))
            throw new InvalidOperationException(string.Format("No resolver for {0}", source));
        return resolver(source);
    }
    public static void RegisterResolver(string source, Func<string, string> resolver)
    {
        _resolverDict.AddOrUpdate(source, resolver, (s, c) => resolver);
    }

    static PostalCodeAttribute()
    {
        // necessary to enable client side validation
        DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(PostalCodeAttribute), typeof(RegularExpressionAttributeAdapter));
    }

    public PostalCodeAttribute(string patternSource)
        : base(Resolve(patternSource))
    {
    }
}

/// ...

public void SomeIntializer()
{
    PostalCodeAttribute.RegisterResolver("db_source", (s) => PostalCodeRegularExpressions.LookupFromDatabase());
}

public class SomeClassWithDataValidation
{
    [PostalCode("db_source")]
    public string PostalCode { get; set; }
}

请注意,只有在实例化任何这些属性之前完成匹配的解析程序功能的注册时,这才有效。