简体   繁体   English

我如何调用从通用接口派生的属性的方法,而该接口的具体类型未知?

[英]How do I call a method of an attribute derived from a generic interface, where the specific type is not known?

Core Question: 核心问题:
I have a generic interface IValidatingAttribute<T> , which creates the contract bool IsValid(T value); 我有一个通用接口IValidatingAttribute<T> ,它创建合同bool IsValid(T value); The interface is implemented by a variety of Attributes, which all serve the purpose of determining if the current value of said Field or Property they decorate is valid per the interface spec that I'm dealing with. 该接口由各种属性实现,所有这些属性的目的是确定我装饰的所述字段或属性的当前值是否符合我正在处理的接口规范。 What I want to do is create a single validation method that will scan every field and property of the given model, and if that field or property has any attributes that implement IValidatingAttribute<T> , it should validate the value against each of those attributes. 我想做的是创建一个单一的验证方法,该方法将扫描给定模型的每个字段和属性,并且如果该字段或​​属性具有实现IValidatingAttribute<T>任何属性,则应针对每个这些属性验证值。 So, using reflection I have the sets of fields and properties, and within those sets I can get the list of attributes. 因此,使用反射,我得到了字段和属性的集合,在这些集合中,我可以获得属性的列表。 How can I determine which attributes implement IValidatingAttribute and then call IsValid(T value)? 如何确定哪些属性实现IValidatingAttribute,然后调用IsValid(T value)?

background : 背景
I am working on a library project that will be used to develop a range of later projects against the interface for a common third party system. 我正在开发一个图书馆项目,该项目将用于针对通用第三方系统的界面开发一系列后续项目。 (BL Server, for those interested) (BL服务器,对于那些感兴趣的人)

BL Server has a wide range of fairly arcane command structures that have varying validation requirements per command and parameter, and then it costs per transaction to call these commands, so one of the library requirements is to easily define the valdiation requirements at the model level to catch invalid commands before they are sent. BL Server具有各种相当神秘的命令结构,这些结构对每个命令和参数的验证要求都有所不同,因此调用这些命令的每次交易成本都很高,因此,库要求之一就是在模型级别轻松定义验证要求,以达到以下目的:在发送无效命令之前捕获它们。 It is also intended to aid in the development of later projects by allowing developers to catch invalid models without needing to set up the BL server connections. 它还旨在通过允许开发人员捕获无效模型而无需建立BL服务器连接来帮助以后的项目开发。

Current Attempt: 当前尝试:
Here's where I've gotten so far (IsValid is an extension method): 到目前为止,这是我到过的地方(IsValid是扩展方法):

public interface IValidatingAttribute<T>
{
    bool IsValid(T value);
}

public static bool IsValid<TObject>(this TObject sourceObject) where TObject : class, new()
{
    var properties = typeof(TObject).GetProperties();
    foreach (var prop in properties)
    {
        var attributeData = prop.GetCustomAttributesData();
        foreach (var attribute in attributeData)
        {
            var attrType = attribute.AttributeType;
            var interfaces = attrType.GetInterfaces().Where(inf => inf.IsGenericType).ToList();
            if (interfaces.Any(infc => infc.Equals(typeof(IValidatingAttribute<>))))
            {
                var value = prop.GetValue(sourceObject);

                //At this point, I know that the current attribute implements 'IValidatingAttribute<>', but I don't know what T is in that implementation.
                //Also, I don't know what data type 'value' is, as it's currently boxed as an object.
                //The underlying type to value will match the expected T in IValidatingAttribute.
                //What I need is something like the line below:
                if (!(attribute as IValidatingAttribute<T>).IsValid(value as T)) //I know this condition doesn't work, but it's what I'm trying to do.
                {
                    return false;
                }
            }
        }
        return true;
    }
}

Example usage : 用法示例
Just to better explain what I am trying to achieve: 为了更好地解释我要达到的目标:

public class SomeBLRequestObject
{
    /// <summary>
    /// Required, only allows exactly 2 alpha characters.
    /// </summary>
    [MinCharacterCount(2), MaxCharacterCount(2), IsRequired, AllowedCharacterSet(CharSets.Alpha))]
    public string StateCode {get; set;}
}

And then, later on in code: 然后,稍后在代码中:

...
var someBLObj = SomeBLRequestObjectFactory.Create();
if(!someBLObj.IsValid())
{
    throw new InvalidObjectException("someBLObj is invalid!");
}

Thank you , I'm really looking for a solution to the problem as it stands, but I'm more than willing to listen if somebody has a viable alternative approach. 谢谢 ,我真的在寻找解决问题的方法,但是如果有人有可行的替代方法,我非常愿意听。

I'm trying to go generic extension method with this because there are literally hundreds of the BL Server objects, and I'm going with attributes because each of these objects can have upper double digit numbers of properties, and it's going to make things much, much easier if the requirements for each object are backed in and nice and readable for the next developer to have to use this thing. 我正在尝试使用通用扩展方法,因为实际上有数百个BL Server对象,而我正在使用属性,因为这些对象中的每一个都可以具有高两位数的属性,这将使事情变得很多,如果备份了每个对象的要求,并且容易理解,便于下一个开发人员使用此工具,则容易得多。

Edit Forgot to mention : This Question is the closest I've found, but what I really need are the contents of \\\\Do Something in TcKs's answer. 编辑忘记提及: 该问题是我找到的最接近的问题 ,但我真正需要的是TcK答案中\\\\Do Something的内容。

Well, after about 6 hours and a goods nights sleep, I realized that I was over-complicating this thing. 好吧,经过大约6个小时的睡眠后,我意识到我正在使这件事变得过于复杂。 Solved it with the following ( ExtValidationInfo is the class that the below two extensions are in.): 使用以下解决方案( ExtValidationInfo是以下两个扩展所在的类。):

Jon Skeet's answer over here pointed me at a better approach, although it still smells a bit, this one at least works. 乔恩·斯基特(Jon Skeet)在这里的回答为我提供了一种更好的方法,尽管它仍然闻起来有点香,但这至少是可行的。

    public static bool IsValid<TObject>(this TObject sourceObject) where TObject : class, new()
    {
        var baseValidationMethod = typeof(ExtValidationInfo).GetMethod("ValidateProperty", BindingFlags.Static | BindingFlags.Public);

        var properties = TypeDataHandler<TObject>.Properties;
        foreach (var prop in properties)
        {
            var attributes = prop.GetCustomAttributes(typeof(IValidatingAttribute<>)).ToList();

            if (!attributes.Any())
            {
                continue; // No validators, skip.
            }


            var propType = prop.PropertyType;
            var validationMethod = baseValidationMethod.MakeGenericMethod(propType);

            var propIsValid = validationMethod.Invoke(null, prop.GetValue(sourceObject), attributes);
            if(!propIsValid)
            {
                return false;
            }
        }
        return true;
    }

    public static bool ValidateProperty<TPropType>(TPropType value, List<IValidatingAttribute<TPropType>> validators)
    {
        foreach (var validator in validators)
        {
            if (!validator.IsValid(value))
            {
                return false;
            }
        }
        return true;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM