4

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

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

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

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

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

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

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

4

2 回答 2

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);
    }
}
于 2013-08-20T17:17:49.137 回答
0

如几个相关问题所示(例如,将 Class 实例作为参数传递给 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; }
}

请注意,这仅适用于在实例化任何这些属性之前完成匹配解析器函数的注册。

于 2013-08-20T18:06:59.023 回答