Beispiel #1
0
        private Expression GetPredicateBody(HttpRequestMessage request, ParameterExpression param)
        {
            Expression workingExpr = null;

            var type       = param.Type;
            var queryPairs = request.GetQueryNameValuePairs();

            foreach (var queryPair in queryPairs)
            {
                if (String.IsNullOrWhiteSpace(queryPair.Key))
                {
                    continue;
                }

                var prop = _modelManager.GetPropertyForJsonKey(type, queryPair.Key);

                if (prop != null)
                {
                    var propertyType = prop.PropertyType;

                    var queryValue = queryPair.Value;
                    if (string.IsNullOrWhiteSpace(queryValue))
                    {
                        queryValue = null;
                    }

                    Expression expr = null;
                    if (propertyType == typeof(String))
                    {
                        if (String.IsNullOrWhiteSpace(queryValue))
                        {
                            Expression propertyExpr = Expression.Property(param, prop);
                            expr = Expression.Equal(propertyExpr, Expression.Constant(null));
                        }
                        else
                        {
                            Expression propertyExpr = Expression.Property(param, prop);
                            expr = Expression.Equal(propertyExpr, Expression.Constant(queryValue));
                        }
                    }
                    else if (propertyType == typeof(Boolean))
                    {
                        bool value;
                        expr = bool.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(Boolean?))
                    {
                        bool tmp;
                        var  value = bool.TryParse(queryValue, out tmp) ? tmp : (bool?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(SByte))
                    {
                        SByte value;
                        expr = SByte.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(SByte?))
                    {
                        SByte tmp;
                        var   value = SByte.TryParse(queryValue, out tmp) ? tmp : (SByte?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(Byte))
                    {
                        Byte value;
                        expr = Byte.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(Byte?))
                    {
                        Byte tmp;
                        var  value = Byte.TryParse(queryValue, out tmp) ? tmp : (Byte?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(Int16))
                    {
                        Int16 value;
                        expr = Int16.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(Int16?))
                    {
                        Int16 tmp;
                        var   value = Int16.TryParse(queryValue, out tmp) ? tmp : (Int16?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(UInt16))
                    {
                        UInt16 value;
                        expr = UInt16.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(UInt16?))
                    {
                        UInt16 tmp;
                        var    value = UInt16.TryParse(queryValue, out tmp) ? tmp : (UInt16?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(Int32))
                    {
                        Int32 value;
                        expr = Int32.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(Int32?))
                    {
                        Int32 tmp;
                        var   value = Int32.TryParse(queryValue, out tmp) ? tmp : (Int32?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(UInt32))
                    {
                        UInt32 value;
                        expr = UInt32.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(UInt32?))
                    {
                        UInt32 tmp;
                        var    value = UInt32.TryParse(queryValue, out tmp) ? tmp : (UInt32?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(Int64))
                    {
                        Int64 value;
                        expr = Int64.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(Int64?))
                    {
                        Int64 tmp;
                        var   value = Int64.TryParse(queryValue, out tmp) ? tmp : (Int64?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(UInt64))
                    {
                        UInt64 value;
                        expr = UInt64.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(UInt64?))
                    {
                        UInt64 tmp;
                        var    value = UInt64.TryParse(queryValue, out tmp) ? tmp : (UInt64?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(Single))
                    {
                        Single value;
                        expr = Single.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(Single?))
                    {
                        Single tmp;
                        var    value = Single.TryParse(queryValue, out tmp) ? tmp : (Single?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(Double))
                    {
                        Double value;
                        expr = Double.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(Double?))
                    {
                        Double tmp;
                        var    value = Double.TryParse(queryValue, out tmp) ? tmp : (Double?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(Decimal))
                    {
                        Decimal value;
                        expr = Decimal.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(Decimal?))
                    {
                        Decimal tmp;
                        var     value = Decimal.TryParse(queryValue, out tmp) ? tmp : (Decimal?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(DateTime))
                    {
                        DateTime value;
                        expr = DateTime.TryParse(queryValue, out value)
                            ? GetPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(DateTime?))
                    {
                        DateTime tmp;
                        var      value = DateTime.TryParse(queryValue, out tmp) ? tmp : (DateTime?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType == typeof(DateTimeOffset))
                    {
                        DateTimeOffset value;
                        expr = DateTimeOffset.TryParse(queryValue, out value)
                            ? GetPropertyExpression <DateTimeOffset>(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType == typeof(DateTimeOffset?))
                    {
                        DateTimeOffset tmp;
                        var            value = DateTimeOffset.TryParse(queryValue, out tmp) ? tmp : (DateTimeOffset?)null;
                        expr = GetPropertyExpression(value, prop, param);
                    }
                    else if (propertyType.IsEnum)
                    {
                        int value;
                        expr = (int.TryParse(queryValue, out value) && Enum.IsDefined(propertyType, value))
                            ? GetEnumPropertyExpression(value, prop, param)
                            : Expression.Constant(false);
                    }
                    else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>) &&
                             propertyType.GenericTypeArguments[0].IsEnum)
                    {
                        int tmp;
                        var value = int.TryParse(queryValue, out tmp) ? tmp : (int?)null;
                        expr = GetEnumPropertyExpression(value, prop, param);
                    }
                    else
                    {
                        // See if it is a relationship property
                        if (_modelManager.IsSerializedAsMany(propertyType))
                        {
                            var          elementType = _modelManager.GetElementType(propertyType);
                            PropertyInfo relatedIdProperty;
                            try
                            {
                                relatedIdProperty = _modelManager.GetIdProperty(elementType);
                            }
                            catch (InvalidOperationException)
                            {
                                relatedIdProperty = null;
                            }

                            if (relatedIdProperty != null)
                            {
                                var propertyExpr = Expression.Property(param, prop);

                                if (string.IsNullOrWhiteSpace(queryValue))
                                {
                                    var leftExpr = Expression.Equal(propertyExpr, Expression.Constant(null));

                                    var asQueryableCallExpr = Expression.Call(
                                        typeof(Queryable),
                                        "AsQueryable",
                                        new[] { elementType },
                                        propertyExpr);
                                    var anyCallExpr = Expression.Call(
                                        typeof(Queryable),
                                        "Any",
                                        new[] { elementType },
                                        asQueryableCallExpr);
                                    var rightExpr = Expression.Not(anyCallExpr);

                                    expr = Expression.OrElse(leftExpr, rightExpr);
                                }
                                else
                                {
                                    var leftExpr = Expression.NotEqual(propertyExpr, Expression.Constant(null));

                                    var idValue  = queryValue.Trim();
                                    var idExpr   = Expression.Constant(idValue);
                                    var anyParam = Expression.Parameter(elementType);
                                    var relatedIdPropertyExpr         = Expression.Property(anyParam, relatedIdProperty);
                                    var relatedIdPropertyEqualsIdExpr = Expression.Equal(relatedIdPropertyExpr, idExpr);
                                    var anyPredicateExpr    = Expression.Lambda(relatedIdPropertyEqualsIdExpr, anyParam);
                                    var asQueryableCallExpr = Expression.Call(
                                        typeof(Queryable),
                                        "AsQueryable",
                                        new[] { elementType },
                                        propertyExpr);
                                    var rightExpr = Expression.Call(
                                        typeof(Queryable),
                                        "Any",
                                        new[] { elementType },
                                        asQueryableCallExpr,
                                        anyPredicateExpr);

                                    expr = Expression.AndAlso(leftExpr, rightExpr);
                                }
                            }
                        }
                        else
                        {
                            PropertyInfo relatedIdProperty;
                            try
                            {
                                relatedIdProperty = _modelManager.GetIdProperty(propertyType);
                            }
                            catch (InvalidOperationException)
                            {
                                relatedIdProperty = null;
                            }

                            if (relatedIdProperty != null)
                            {
                                var propertyExpr = Expression.Property(param, prop);

                                if (string.IsNullOrWhiteSpace(queryValue))
                                {
                                    expr = Expression.Equal(propertyExpr, Expression.Constant(null));
                                }
                                else
                                {
                                    var leftExpr = Expression.NotEqual(propertyExpr, Expression.Constant(null));

                                    var idValue = queryValue.Trim();
                                    var idExpr  = Expression.Constant(idValue);
                                    var relatedIdPropertyExpr = Expression.Property(propertyExpr, relatedIdProperty);
                                    var rightExpr             = Expression.Equal(relatedIdPropertyExpr, idExpr);

                                    expr = Expression.AndAlso(leftExpr, rightExpr);
                                }
                            }
                        }
                    }

                    if (expr == null)
                    {
                        expr = Expression.Constant(true);
                    }

                    workingExpr = workingExpr == null ? expr : Expression.AndAlso(workingExpr, expr);
                }
            }

            return(workingExpr ?? Expression.Constant(true)); // No filters, so return everything
        }
        protected void Serialize(object value, Stream writeStream, JsonWriter writer, JsonSerializer serializer, RelationAggregator aggregator)
        {
            writer.WriteStartObject();

            // Do the Id now...
            writer.WritePropertyName("id");
            var idProp = _modelManager.GetIdProperty(value.GetType());

            writer.WriteValue(GetValueForIdProperty(idProp, value));

            // Leverage the cached map to avoid another costly call to System.Type.GetProperties()
            PropertyInfo[] props = _modelManager.GetProperties(value.GetType());

            // Do non-model properties first, everything else goes in "links"
            //TODO: Unless embedded???
            IList <PropertyInfo> modelProps = new List <PropertyInfo>();

            foreach (PropertyInfo prop in props)
            {
                if (prop == idProp)
                {
                    continue;
                }

                if (this.CanWriteTypeAsPrimitive(prop.PropertyType))
                {
                    if (prop.GetCustomAttributes().Any(attr => attr is JsonIgnoreAttribute))
                    {
                        continue;
                    }

                    // numbers, strings, dates...
                    writer.WritePropertyName(_modelManager.GetJsonKeyForProperty(prop));

                    var propertyValue = prop.GetValue(value, null);

                    if (prop.PropertyType == typeof(string) &&
                        prop.GetCustomAttributes().Any(attr => attr is SerializeStringAsRawJsonAttribute))
                    {
                        if (propertyValue == null)
                        {
                            writer.WriteNull();
                        }
                        else
                        {
                            var minifiedValue = JsonHelpers.MinifyJson((string)propertyValue);
                            writer.WriteRawValue(minifiedValue);
                        }
                    }
                    else
                    {
                        serializer.Serialize(writer, propertyValue);
                    }
                }
                else
                {
                    modelProps.Add(prop);
                    continue;
                }
            }

            // Now do other stuff
            if (modelProps.Count() > 0)
            {
                writer.WritePropertyName("links");
                writer.WriteStartObject();
            }
            foreach (PropertyInfo prop in modelProps)
            {
                bool               skip = false, iip = false;
                string             lt = null;
                SerializeAsOptions sa = SerializeAsOptions.Ids;

                object[] attrs = prop.GetCustomAttributes(true);

                foreach (object attr in attrs)
                {
                    Type attrType = attr.GetType();
                    if (typeof(JsonIgnoreAttribute).IsAssignableFrom(attrType))
                    {
                        skip = true;
                        continue;
                    }
                    if (typeof(IncludeInPayload).IsAssignableFrom(attrType))
                    {
                        iip = ((IncludeInPayload)attr).Include;
                    }
                    if (typeof(SerializeAs).IsAssignableFrom(attrType))
                    {
                        sa = ((SerializeAs)attr).How;
                    }
                    if (typeof(LinkTemplate).IsAssignableFrom(attrType))
                    {
                        lt = ((LinkTemplate)attr).TemplateString;
                    }
                }
                if (skip)
                {
                    continue;
                }

                writer.WritePropertyName(_modelManager.GetJsonKeyForProperty(prop));

                // Look out! If we want to SerializeAs a link, computing the property is probably
                // expensive...so don't force it just to check for null early!
                if (sa != SerializeAsOptions.Link && prop.GetValue(value, null) == null)
                {
                    writer.WriteNull();
                    continue;
                }

                // Now look for enumerable-ness:
                if (typeof(IEnumerable <Object>).IsAssignableFrom(prop.PropertyType))
                {
                    switch (sa)
                    {
                    case SerializeAsOptions.Ids:
                        //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                        IEnumerable <object> items = (IEnumerable <object>)prop.GetValue(value, null);
                        if (items == null)
                        {
                            writer.WriteValue((IEnumerable <object>)null); //TODO: Is it okay with the spec and Ember Data to return null for an empty array?
                            break;                                         // LOOK OUT! Ending this case block early here!!!
                        }
                        this.WriteIdsArrayJson(writer, items, serializer);
                        if (iip)
                        {
                            Type itemType;
                            if (prop.PropertyType.IsGenericType)
                            {
                                itemType = prop.PropertyType.GetGenericArguments()[0];
                            }
                            else
                            {
                                // Must be an array at this point, right??
                                itemType = prop.PropertyType.GetElementType();
                            }
                            if (aggregator != null)
                            {
                                aggregator.Add(itemType, items);                         // should call the IEnumerable one...right?
                            }
                        }
                        break;

                    case SerializeAsOptions.Link:
                        if (lt == null)
                        {
                            throw new JsonSerializationException("A property was decorated with SerializeAs(SerializeAsOptions.Link) but no LinkTemplate attribute was provided.");
                        }
                        //TODO: Check for "{0}" in linkTemplate and (only) if it's there, get the Ids of all objects and "implode" them.
                        string href = String.Format(lt, null, GetIdFor(value));
                        //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                        //TODO: Support ids and type properties in "link" object
                        writer.WriteStartObject();
                        writer.WritePropertyName("href");
                        writer.WriteValue(href);
                        writer.WriteEndObject();
                        break;

                    case SerializeAsOptions.Embedded:
                        // Not really supported by Ember Data yet, incidentally...but easy to implement here.
                        //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                        //serializer.Serialize(writer, prop.GetValue(value, null));
                        this.Serialize(prop.GetValue(value, null), writeStream, writer, serializer, aggregator);
                        break;
                    }
                }
                else
                {
                    var propertyValue = prop.GetValue(value, null);
                    if (propertyValue == null)
                    {
                        writer.WriteNull();
                    }
                    else
                    {
                        string objId = GetIdFor(propertyValue);

                        switch (sa)
                        {
                        case SerializeAsOptions.Ids:
                            //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                            serializer.Serialize(writer, objId);
                            if (iip)
                            {
                                if (aggregator != null)
                                {
                                    aggregator.Add(prop.PropertyType, prop.GetValue(value, null));
                                }
                            }
                            break;

                        case SerializeAsOptions.Link:
                            if (lt == null)
                            {
                                throw new JsonSerializationException(
                                          "A property was decorated with SerializeAs(SerializeAsOptions.Link) but no LinkTemplate attribute was provided.");
                            }
                            string link = String.Format(lt, objId,
                                                        GetIdFor(value)); //value.GetType().GetProperty("Id").GetValue(value, null));

                            //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                            writer.WriteStartObject();
                            writer.WritePropertyName("href");
                            writer.WriteValue(link);
                            writer.WriteEndObject();
                            break;

                        case SerializeAsOptions.Embedded:
                            // Not really supported by Ember Data yet, incidentally...but easy to implement here.
                            //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                            //serializer.Serialize(writer, prop.GetValue(value, null));
                            this.Serialize(prop.GetValue(value, null), writeStream, writer, serializer, aggregator);
                            break;
                        }
                    }
                }
            }
            if (modelProps.Count() > 0)
            {
                writer.WriteEndObject();
            }

            writer.WriteEndObject();
        }