public Task <IBinding> TryCreateAsync(BindingProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            // Determine whether we should bind to the current parameter
            ParameterInfo       parameter = context.Parameter;
            ClassifierAttribute attribute = parameter.GetCustomAttribute <ClassifierAttribute>(inherit: false);

            if (attribute == null)
            {
                return(Task.FromResult <IBinding>(null));
            }

            // What data type(s) can this attribute be attached to?
            IEnumerable <Type> supportedTypes = new Type[] { typeof(Classifier) };

            if (!(parameter.ParameterType == typeof(Classifier)))
            {
                throw new InvalidOperationException(
                          $"Can't bind ClassifierAttribute to type '{parameter.ParameterType}'.");
            }

            return(Task.FromResult <IBinding>(new ClassifierAttributeBinding(parameter)));
        }
        /// <summary>
        /// Creates a new reflection based classifier
        /// </summary>
        public JsonReflectionClassifier(Type type)
        {
            this.m_type = type;
            var classifierAtt = type.StripGeneric().GetTypeInfo().GetCustomAttribute <ClassifierAttribute>();

            this.m_classifierAttribute = classifierAtt;
        }
Example #3
0
        /// <summary>
        /// Creates a new reflection based classifier
        /// </summary>
        public JsonReflectionClassifier(Type type, IViewModelSerializer owner)
        {
            this.m_type = type;
            var classifierAtt = type.StripGeneric().GetCustomAttribute <ClassifierAttribute>();

            this.m_classifierAttribute = classifierAtt;
            this.m_serializer          = owner;
        }
Example #4
0
        /// <summary>
        /// Create a classifier instance from the classifier attribute tagging the imput parameter
        /// </summary>
        /// <param name="attribute">
        /// The ClassifierAttribute defining the classifier to create
        /// </param>
        /// <param name="context">
        /// The context within which this binding is occuring
        /// </param>
        public static Task <Classifier> BuildClassifierFromAttribute(ClassifierAttribute attribute,
                                                                     ValueBindingContext context)
        {
            // If possible get the connection string to use

            // Use this and the attribute to create a new classifier instance
            return(Task <Classifier> .FromResult(new Classifier(attribute)));
        }
Example #5
0
        /// <summary>
        /// Get classifier object
        /// </summary>
        private object GetClassifierObj(object o, ClassifierAttribute classifierAttribute)
        {
            if (o == null)
            {
                return(null);
            }

            var classProperty = o.GetType().GetRuntimeProperty(classifierAttribute.ClassifierProperty);
            var classifierObj = classProperty.GetValue(o);

            if (classifierObj == null)
            {
                // Force load
                var keyPropertyName = classProperty.GetCustomAttribute <SerializationReferenceAttribute>()?.RedirectProperty;
                if (keyPropertyName == null)
                {
                    return(null);
                }
                var keyPropertyValue = o.GetType().GetRuntimeProperty(keyPropertyName).GetValue(o);

                // Does the owner serializer already load this?
                if (keyPropertyValue != null)
                {
                    classifierObj = this.m_serializer.GetLoadedObject((Guid)keyPropertyValue);
                }
                if (classifierObj == null)
                {
                    // Now we want to force load!!!!
                    var getValueMethod = typeof(EntitySource).GetGenericMethod("Get", new Type[] { classProperty.PropertyType }, new Type[] { typeof(Guid?) });
                    classifierObj = getValueMethod.Invoke(EntitySource.Current, new object[] { keyPropertyValue });
                    classProperty.SetValue(o, classifierObj);
                    if (keyPropertyValue != null)
                    {
                        this.m_serializer.AddLoadedObject((Guid)keyPropertyValue, (IdentifiedData)classifierObj);
                    }
                }
            }

            if (classifierObj != null)
            {
                if (!m_classifierCache.TryGetValue(classifierObj.GetType(), out classifierAttribute))
                {
                    lock (m_classifierCache)
                        if (!m_classifierCache.ContainsKey(classifierObj.GetType()))
                        {
                            classifierAttribute = classifierObj?.GetType().GetCustomAttribute <ClassifierAttribute>();
                            m_classifierCache.Add(classifierObj.GetType(), classifierObj.GetType().GetCustomAttribute <ClassifierAttribute>());
                        }
                }
            }

            if (classifierAttribute != null)
            {
                return(this.GetClassifierObj(classifierObj, classifierAttribute));
            }
            return(classifierObj);
        }
        /// <summary>
        /// Create the classifier from the attribute assigned to it in the function
        /// parameters
        /// </summary>
        /// <param name="attribute">
        /// The attribute that describes the classifier to use
        /// </param>
        public Classifier(ClassifierAttribute attribute,
                          string connectionStringName = @"")
        {
            _domainName           = attribute.DomainName;
            _aggregateTypeName    = attribute.AggregateTypeName;
            _aggregateInstanceKey = attribute.InstanceKey;
            _classifierTypeName   = attribute.ClassifierTypeName;

            if (string.IsNullOrWhiteSpace(connectionStringName))
            {
                _connectionStringName = ConnectionStringNameAttribute.DefaultConnectionStringName(attribute);
            }
            else
            {
                _connectionStringName = connectionStringName;
            }
        }
        public async Task <object> GetValueAsync()
        {
            object item = null;

            await ValidateParameter(_parameter);

            if (null != _parameter)
            {
                ClassifierAttribute attribute = _parameter.GetCustomAttribute <ClassifierAttribute>(inherit: false);
                if (null != attribute)
                {
                    item = new Classifier(attribute);
                }
            }

            return(item);
        }
Example #8
0
        /// <summary>
        /// Build LINQ expression
        /// </summary>
        public static LambdaExpression BuildLinqExpression <TModelType>(NameValueCollection httpQueryParameters, string parameterName, Dictionary <String, Delegate> variables = null, bool safeNullable = true)
        {
            var        parameterExpression = Expression.Parameter(typeof(TModelType), parameterName);
            Expression retVal = null;
            List <KeyValuePair <String, String[]> > workingValues = new List <KeyValuePair <string, string[]> >();

            // Iterate
            foreach (var nvc in httpQueryParameters.Where(p => !p.Key.StartsWith("_")).Distinct())
            {
                workingValues.Add(new KeyValuePair <string, string[]>(nvc.Key, nvc.Value.ToArray()));
            }

            // Get the first values
            while (workingValues.Count > 0)
            {
                var currentValue = workingValues.FirstOrDefault();
                workingValues.Remove(currentValue);

                if (currentValue.Value.Count(o => !String.IsNullOrEmpty(o)) == 0)
                {
                    continue;
                }

                // Create accessor expression
                Expression keyExpression    = null;
                Expression accessExpression = parameterExpression;
                String[]   memberPath       = currentValue.Key.Split('.');
                String     path             = "";

                foreach (var rawMember in memberPath)
                {
                    var    pMember = rawMember;
                    String guard   = String.Empty,
                           cast    = String.Empty;

                    // Update path
                    path += pMember + ".";
                    bool coalesce = false;

                    // Guard token?
                    if (pMember.Contains("[") && pMember.EndsWith("]"))
                    {
                        guard   = pMember.Substring(pMember.IndexOf("[") + 1, pMember.Length - pMember.IndexOf("[") - 2);
                        pMember = pMember.Substring(0, pMember.IndexOf("["));
                    }
                    if (pMember.EndsWith("?"))
                    {
                        coalesce = true;
                        pMember  = pMember.Substring(0, pMember.Length - 1);
                    }
                    if (pMember.Contains("@"))
                    {
                        cast    = pMember.Substring(pMember.IndexOf("@") + 1);
                        pMember = pMember.Substring(0, pMember.IndexOf("@"));
                    }

                    // Get member cache for data
                    Dictionary <String, PropertyInfo> memberCache = null;
                    if (!m_memberCache.TryGetValue(accessExpression.Type, out memberCache))
                    {
                        memberCache = new Dictionary <string, PropertyInfo>();
                        lock (m_memberCache)
                            if (!m_memberCache.ContainsKey(accessExpression.Type))
                            {
                                m_memberCache.Add(accessExpression.Type, memberCache);
                            }
                    }

                    // Add member info
                    PropertyInfo memberInfo = null;
                    if (!memberCache.TryGetValue(pMember, out memberInfo))
                    {
                        memberInfo = accessExpression.Type.GetRuntimeProperties().FirstOrDefault(p => p.GetCustomAttributes <XmlElementAttribute>()?.Any(a => a.ElementName == pMember) == true);
                        if (memberInfo == null)
                        {
                            throw new ArgumentOutOfRangeException(currentValue.Key);
                        }

                        // Member cache
                        lock (memberCache)
                            if (!memberCache.ContainsKey(pMember))
                            {
                                memberCache.Add(pMember, memberInfo);
                            }
                    }

                    // Handle XML props
                    if (memberInfo.Name.EndsWith("Xml"))
                    {
                        memberInfo = accessExpression.Type.GetRuntimeProperty(memberInfo.Name.Replace("Xml", ""));
                    }
                    else if (pMember != memberPath.Last())
                    {
                        PropertyInfo backingFor = null;

                        // Look in member cache
                        if (!m_redirectCache.TryGetValue(accessExpression.Type, out memberCache))
                        {
                            memberCache = new Dictionary <string, PropertyInfo>();
                            lock (m_redirectCache)
                                if (!m_redirectCache.ContainsKey(accessExpression.Type))
                                {
                                    m_redirectCache.Add(accessExpression.Type, memberCache);
                                }
                        }

                        // Now find backing
                        if (!memberCache.TryGetValue(pMember, out backingFor))
                        {
                            backingFor = accessExpression.Type.GetRuntimeProperties().FirstOrDefault(p => p.GetCustomAttribute <SerializationReferenceAttribute>()?.RedirectProperty == memberInfo.Name);
                            // Member cache
                            lock (memberCache)
                                if (!memberCache.ContainsKey(pMember))
                                {
                                    memberCache.Add(pMember, backingFor);
                                }
                        }

                        if (backingFor != null)
                        {
                            memberInfo = backingFor;
                        }
                    }

                    accessExpression = Expression.MakeMemberAccess(accessExpression, memberInfo);


                    if (!String.IsNullOrEmpty(cast))
                    {
                        Type castType = null;
                        if (!m_castCache.TryGetValue(cast, out castType))
                        {
                            castType = typeof(QueryExpressionParser).GetTypeInfo().Assembly.ExportedTypes.FirstOrDefault(o => o.GetTypeInfo().GetCustomAttribute <XmlTypeAttribute>()?.TypeName == cast);
                            if (castType == null)
                            {
                                throw new ArgumentOutOfRangeException(nameof(castType), cast);
                            }

                            lock (m_castCache)
                                if (!m_castCache.ContainsKey(cast))
                                {
                                    m_castCache.Add(cast, castType);
                                }
                        }
                        accessExpression = Expression.TypeAs(accessExpression, castType);
                    }
                    if (coalesce)
                    {
                        accessExpression = Expression.Coalesce(accessExpression, Expression.New(accessExpression.Type));
                    }

                    // Guard on classifier?
                    if (!String.IsNullOrEmpty(guard))
                    {
                        Type itemType      = accessExpression.Type.GenericTypeArguments[0];
                        Type predicateType = typeof(Func <,>).MakeGenericType(itemType, typeof(bool));
                        ParameterExpression guardParameter = Expression.Parameter(itemType, "guard");
                        if (guard == "null")
                        {
                            guard = null;
                        }

                        // Cascade the Classifiers to get the access
                        ClassifierAttribute classAttr = itemType.GetTypeInfo().GetCustomAttribute <ClassifierAttribute>();
                        if (classAttr == null)
                        {
                            throw new InvalidOperationException("No classifier found for guard expression");
                        }
                        PropertyInfo classifierProperty = itemType.GetRuntimeProperty(classAttr.ClassifierProperty);
                        // Handle XML props
                        if (classifierProperty.Name.EndsWith("Xml"))
                        {
                            classifierProperty = itemType.GetRuntimeProperty(classifierProperty.Name.Replace("Xml", ""));
                        }

                        Expression guardAccessor = guardParameter;
                        while (classifierProperty != null && classAttr != null)
                        {
                            guardAccessor = Expression.MakeMemberAccess(guardAccessor, classifierProperty);


                            classAttr = classifierProperty.PropertyType.GetTypeInfo().GetCustomAttribute <ClassifierAttribute>();
                            if (classAttr != null && guard != null)
                            {
                                classifierProperty = classifierProperty.PropertyType.GetRuntimeProperty(classAttr.ClassifierProperty);
                            }
                            else if (guard == null)
                            {
                                break;
                            }
                        }

                        MethodInfo whereMethod = typeof(Enumerable).GetGenericMethod("Where",
                                                                                     new Type[] { itemType },
                                                                                     new Type[] { accessExpression.Type, predicateType }) as MethodInfo;

                        // Now make expression
                        Expression guardExpression = null;
                        if (guard != null)
                        {
                            foreach (var g in guard.Split('|'))
                            {
                                var expr = Expression.MakeBinary(ExpressionType.Equal, guardAccessor, Expression.Constant(g));
                                if (guardExpression == null)
                                {
                                    guardExpression = expr;
                                }
                                else
                                {
                                    guardExpression = Expression.MakeBinary(ExpressionType.Or, guardExpression, expr);
                                }
                            }
                        }
                        else
                        {
                            guardExpression = Expression.MakeBinary(ExpressionType.Equal, guardAccessor, Expression.Constant(null));
                        }

                        var guardLambda = Expression.Lambda(guardExpression, guardParameter);
                        accessExpression = Expression.Call(whereMethod, accessExpression, guardLambda);

                        if (currentValue.Value.Length == 1 && currentValue.Value[0].EndsWith("null"))
                        {
                            var anyMethod = typeof(Enumerable).GetGenericMethod("Any",
                                                                                new Type[] { itemType },
                                                                                new Type[] { accessExpression.Type }) as MethodInfo;
                            accessExpression      = Expression.Call(anyMethod, accessExpression);
                            currentValue.Value[0] = currentValue.Value[0].Replace("null", "false");
                        }
                    }
                    // List expression, we want the Any() operator
                    if (accessExpression.Type.GetTypeInfo().ImplementedInterfaces.Any(o => o == typeof(IEnumerable)) &&
                        accessExpression.Type.GetTypeInfo().IsGenericType)
                    {
                        Type itemType      = accessExpression.Type.GenericTypeArguments[0];
                        Type predicateType = typeof(Func <,>).MakeGenericType(itemType, typeof(bool));

                        var anyMethod = typeof(Enumerable).GetGenericMethod("Any",
                                                                            new Type[] { itemType },
                                                                            new Type[] { accessExpression.Type, predicateType }) as MethodInfo;

                        // Add sub-filter
                        NameValueCollection subFilter = new NameValueCollection();
                        subFilter.Add(currentValue.Key.Substring(path.Length), new List <String>(currentValue.Value));

                        // Add collect other parameters
                        foreach (var wv in workingValues.Where(o => o.Key.StartsWith(path)).ToList())
                        {
                            subFilter.Add(wv.Key.Substring(path.Length), new List <String>(wv.Value));
                            workingValues.Remove(wv);
                        }

                        var builderMethod = typeof(QueryExpressionParser).GetGenericMethod(nameof(BuildLinqExpression), new Type[] { itemType }, new Type[] { typeof(NameValueCollection), typeof(String), typeof(Dictionary <String, Delegate>), typeof(bool) });

                        Expression predicate = (builderMethod.Invoke(null, new object[] { subFilter, pMember, variables, safeNullable }) as LambdaExpression);
                        if (predicate == null)
                        {
                            continue;
                        }
                        keyExpression = Expression.Call(anyMethod, accessExpression, predicate);
                        currentValue  = new KeyValuePair <string, string[]>();
                        break;  // skip
                    }
                }

                // Now expression
                var kp = currentValue.Value;
                if (kp != null)
                {
                    foreach (var qValue in kp.Where(o => !String.IsNullOrEmpty(o)))
                    {
                        var value = qValue;
                        var thisAccessExpression = accessExpression;
                        // HACK: Fuzz dates for intervals
                        if ((thisAccessExpression.Type.StripNullable() == typeof(DateTime) ||
                             thisAccessExpression.Type.StripNullable() == typeof(DateTimeOffset)) &&
                            value.Length <= 7 &&
                            !value.StartsWith("~") &&
                            !value.Contains("null")
                            )
                        {
                            value = "~" + value;
                        }

                        Expression nullCheckExpr = null;

                        // Correct for nullable
                        if (value != "null" && thisAccessExpression.Type.GetTypeInfo().IsGenericType&& thisAccessExpression.Type.GetGenericTypeDefinition() == typeof(Nullable <>) &&
                            safeNullable)
                        {
                            nullCheckExpr        = Expression.MakeBinary(ExpressionType.NotEqual, thisAccessExpression, Expression.Constant(null));
                            thisAccessExpression = Expression.MakeMemberAccess(thisAccessExpression, accessExpression.Type.GetRuntimeProperty("Value"));
                        }

                        // Process value
                        String         pValue = value;
                        ExpressionType et     = ExpressionType.Equal;

                        if (String.IsNullOrEmpty(value))
                        {
                            continue;
                        }

                        switch (value[0])
                        {
                        case '<':
                            et     = ExpressionType.LessThan;
                            pValue = value.Substring(1);
                            if (pValue[0] == '=')
                            {
                                et     = ExpressionType.LessThanOrEqual;
                                pValue = pValue.Substring(1);
                            }
                            break;

                        case '>':
                            et     = ExpressionType.GreaterThan;
                            pValue = value.Substring(1);
                            if (pValue[0] == '=')
                            {
                                et     = ExpressionType.GreaterThanOrEqual;
                                pValue = pValue.Substring(1);
                            }
                            break;

                        case '^':
                            et = ExpressionType.Equal;
                            if (thisAccessExpression.Type == typeof(String))
                            {
                                thisAccessExpression = Expression.Call(thisAccessExpression, typeof(String).GetRuntimeMethod("StartsWith", new Type[] { typeof(String) }), Expression.Constant(pValue.Substring(1)));
                                pValue = "true";
                            }
                            else
                            {
                                throw new InvalidOperationException("^ can only be applied to string properties");
                            }

                            break;

                        case '~':
                            et = ExpressionType.Equal;
                            if (thisAccessExpression.Type == typeof(String))
                            {
                                thisAccessExpression = Expression.Call(thisAccessExpression, typeof(String).GetRuntimeMethod("Contains", new Type[] { typeof(String) }), Expression.Constant(pValue.Substring(1)));
                                pValue = "true";
                            }
                            else if (thisAccessExpression.Type == typeof(DateTime) ||
                                     thisAccessExpression.Type == typeof(DateTime?))
                            {
                                pValue = value.Substring(1);
                                DateTime dateLow = DateTime.ParseExact(pValue, "yyyy-MM-dd".Substring(0, pValue.Length), CultureInfo.InvariantCulture), dateHigh = DateTime.MaxValue;
                                if (pValue.Length == 4)     // Year
                                {
                                    dateHigh = new DateTime(dateLow.Year, 12, 31, 23, 59, 59);
                                }
                                else if (pValue.Length == 7)
                                {
                                    dateHigh = new DateTime(dateLow.Year, dateLow.Month, DateTime.DaysInMonth(dateLow.Year, dateLow.Month), 23, 59, 59);
                                }
                                else if (pValue.Length == 10)
                                {
                                    dateHigh = new DateTime(dateLow.Year, dateLow.Month, dateLow.Day, 23, 59, 59);
                                }
                                if (thisAccessExpression.Type == typeof(DateTime?))
                                {
                                    thisAccessExpression = Expression.MakeMemberAccess(thisAccessExpression, thisAccessExpression.Type.GetRuntimeProperty("Value"));
                                }
                                Expression lowerBound = Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, thisAccessExpression, Expression.Constant(dateLow)),
                                           upperBound = Expression.MakeBinary(ExpressionType.LessThanOrEqual, thisAccessExpression, Expression.Constant(dateHigh));
                                thisAccessExpression = Expression.MakeBinary(ExpressionType.AndAlso, lowerBound, upperBound);
                                pValue = "true";
                            }
                            else
                            {
                                throw new InvalidOperationException("~ can only be applied to string properties");
                            }

                            break;

                        case '!':
                            et     = ExpressionType.NotEqual;
                            pValue = value.Substring(1);
                            break;
                        }

                        // The expression
                        Expression valueExpr = null;
                        if (pValue == "null")
                        {
                            valueExpr = Expression.Constant(null);
                        }
                        else if (pValue.StartsWith("$"))
                        {
                            Delegate val = null;
                            if (variables.TryGetValue(pValue.Replace("$", ""), out val))
                            {
                                if (val.GetMethodInfo().GetParameters().Length > 0)
                                {
                                    valueExpr = Expression.Invoke(Expression.Constant(val));
                                }
                                else
                                {
                                    valueExpr = Expression.Call(val.Target == null ? null : Expression.Constant(val.Target), val.GetMethodInfo());
                                }
                            }
                            else
                            {
                                valueExpr = Expression.Constant(null);
                            }
                        }
                        else if (thisAccessExpression.Type == typeof(String))
                        {
                            valueExpr = Expression.Constant(pValue);
                        }
                        else if (thisAccessExpression.Type == typeof(DateTime) || thisAccessExpression.Type == typeof(DateTime?))
                        {
                            valueExpr = Expression.Constant(DateTime.Parse(pValue));
                        }
                        else if (thisAccessExpression.Type == typeof(DateTimeOffset) || thisAccessExpression.Type == typeof(DateTimeOffset?))
                        {
                            valueExpr = Expression.Constant(DateTimeOffset.Parse(pValue));
                        }
                        else if (thisAccessExpression.Type == typeof(Guid))
                        {
                            valueExpr = Expression.Constant(Guid.Parse(pValue));
                        }
                        else if (thisAccessExpression.Type == typeof(Guid?))
                        {
                            valueExpr = Expression.Convert(Expression.Constant(Guid.Parse(pValue)), typeof(Guid?));
                        }
                        else if (thisAccessExpression.Type.GetTypeInfo().IsEnum)
                        {
                            int tryParse = 0;
                            if (Int32.TryParse(pValue, out tryParse))
                            {
                                valueExpr = Expression.Constant(Enum.ToObject(thisAccessExpression.Type, Int32.Parse(pValue)));
                            }
                            else
                            {
                                valueExpr = Expression.Constant(Enum.Parse(thisAccessExpression.Type, pValue));
                            }
                        }
                        else
                        {
                            valueExpr = Expression.Constant(Convert.ChangeType(pValue, thisAccessExpression.Type));
                        }
                        if (valueExpr.Type != thisAccessExpression.Type)
                        {
                            valueExpr = Expression.Convert(valueExpr, thisAccessExpression.Type);
                        }
                        Expression singleExpression = Expression.MakeBinary(et, thisAccessExpression, valueExpr);

                        if (nullCheckExpr != null)
                        {
                            singleExpression = Expression.MakeBinary(ExpressionType.AndAlso, nullCheckExpr, singleExpression);
                        }

                        if (keyExpression == null)
                        {
                            keyExpression = singleExpression;
                        }
                        else if (et == ExpressionType.Equal)
                        {
                            keyExpression = Expression.MakeBinary(ExpressionType.OrElse, keyExpression, singleExpression);
                        }
                        else
                        {
                            keyExpression = Expression.MakeBinary(ExpressionType.AndAlso, keyExpression, singleExpression);
                        }
                    }
                }

                if (retVal == null)
                {
                    retVal = keyExpression;
                }
                else
                {
                    retVal = Expression.MakeBinary(ExpressionType.AndAlso, retVal, keyExpression);
                }
            }

            //Debug.WriteLine(String.Format("Converted {0} to {1}", httpQueryParameters, retVal));

            if (retVal == null)
            {
                return(null);
            }
            return(Expression.Lambda(retVal, parameterExpression));
        }