3
\$\begingroup\$

I require that the Feature set of my Licence to have a non-empty intersection with a set of features that the module I am loading contains. I expect other lists in my License to form similar requirements, and I wish to be able to specify as much information as possible on the license object, i.e. I do not wish to need to update the Validator if the contents of the License object changes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;

namespace Testing
{
    public class License
    {
        [CollectionIntersects]
        public List<string> Features { get; set; }       
    }

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public abstract class LicenseValidator:Attribute
    {
        public abstract bool isValid(object context, object obj, PropertyInfo property);
    }

    public class Validator
    {
        public static bool isValid(object context, object obj)
        {
            bool valid = true;
            Type objType = obj.GetType();
            PropertyInfo[] props = objType.GetProperties();
            foreach (PropertyInfo prop in props)
            {
                foreach (LicenseValidator validator in prop.GetCustomAttributes<LicenseValidator>())
                {
                    valid=valid&&validator.isValid(context, obj, prop);
                }
            }
            return valid;
        }
    }

    public class CollectionIntersects: LicenseValidator
    {
        public CollectionIntersects()
        {
        }

        public override bool isValid(object context, object obj, PropertyInfo property)
        {
            Type collectionType = property.PropertyType;
            Object contextvalue = property.GetValue(context);
            Object objectvalue = property.GetValue(obj);

            //Casting code lifted from
            //http://stackoverflow.com/a/31380177/1986513.
            var param = Expression.Parameter(typeof(object));
            dynamic contextvalueenum =
                Expression.Lambda(Expression.Convert(param, collectionType), param).Compile().DynamicInvoke(contextvalue);
            dynamic licencevalueenum =
                Expression.Lambda(Expression.Convert(param, collectionType), param).Compile().DynamicInvoke(objectvalue);

            return ((IEnumerable<object>)Enumerable.Intersect(contextvalueenum, licencevalueenum)).Any();
        }

    }

}

Is there an easier way to accomplish this? Also, how sensitive is it to localization and collation issues considering that the license might be written in a different locale than the reference context object?

\$\endgroup\$
1
\$\begingroup\$

Self answer, one nights sleep later.

A simpler solution can be made by making the arguments of the function dynamic. The isValid method can then be written as;

    public override bool isValid(dynamic context, dynamic obj, PropertyInfo property)
    {
        return ((IEnumerable<object>)Enumerable.Intersect(property.GetValue(context), property.GetValue(obj))).Any();
    }
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.