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?
1 Answer 1
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();
}