我正在创建一个新的Web API,并希望允许用户指定在URL中返回哪些字段。
我目前的想法是:
对于这样的样本模型:
public class Value
{
public string ValueId { get; set; }
public int Number { get; set; }
public ValueInternal Internal { get; set; }
}
public class ValueInternal
{
public int Number { get; set; }
public string Something { get; set; }
}
和这样的网址
http://example.com/api/values/?_fields=Number,Internal(Something)
会返回此
[
{
"Number": 0,
"Internal": {
"Number": 0
}
}
]
我已经提出了以下实现此目的的方法,但它有一些缺陷。即如果Internal
是ValueInternal
的可枚举或者不支持包括所有或包含所有除外,或者如果T和TResult是不同类型,则无法处理。有没有人对我如何改进这一点有任何建议,或者是否已经有一种方法可以解决这个问题。
public static Expression<Func<T, TResult>> CreateSelector<T, TResult>() where TResult : new()
{
var property = "Number,Internal(Something)";
return arg => Process<T, TResult>(arg, default(TResult), property);
}
private static TResult Process<T, TResult>(T arg, TResult output, string propertyList) where TResult : new()
{
if (output == null)
{
output = new TResult();
}
if (string.IsNullOrEmpty(propertyList))
{
return output;
}
var properties = Regex.Split(propertyList, @"(?<!,[^(]+\([^)]+),");
foreach (var property in properties)
{
var propertyName = property;
var propertyInternalsMatch = Regex.Match(property, @"\(.*(?<!,[^(]+\([^)]+)\)");
var internalPropertyList = propertyInternalsMatch.Value;
if (!string.IsNullOrEmpty(internalPropertyList))
{
propertyName = property.Replace(internalPropertyList, "");
internalPropertyList = internalPropertyList.Replace("(", "");
internalPropertyList = internalPropertyList.Replace(")", "");
}
var tProperty = arg.GetType().GetProperty(propertyName);
if(tProperty == null) continue;
var tResultProperty = output.GetType().GetProperty(propertyName);
if(tResultProperty == null) continue;
if (tProperty.PropertyType.IsPrimitive || tProperty.PropertyType.IsValueType || (tProperty.PropertyType == typeof(string)))
{
tResultProperty.SetValue(output, tProperty.GetValue(arg));
}
else
{
var propertyInstance = Activator.CreateInstance(tResultProperty.PropertyType);
tResultProperty.SetValue(output, Process(tProperty.GetValue(arg), propertyInstance, internalPropertyList));
}
}
return output;
}
经过多一点阅读之后,我想我想做一些像这个问题LINQ : Dynamic select的答案,但是我的解决方案仍有同样的缺陷
答案 0 :(得分:2)
如果您在ASP .NET Web API上使用OData支持,则可以使用$select,但如果您不想使用它,或者使用Linq无法轻松查询底层系统,则可以使用自定义合约解析程序,但在这种情况下,您只是减少序列化大小,而不是内部数据流量。
public class FieldsSelectContractResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.GetIsSpecified = (t) =>
{
var fields = HttpContext.Current.Request["fields"];
if (fields != null)
{
return fields.IndexOf(member.Name, StringComparison.OrdinalIgnoreCase) > -1;
}
return true;
};
return property;
}
}
并在WebApiConfig.cs中设置自定义合约解析程序:
var jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.ContractResolver = new FieldsSelectContractResolver();