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