I am creating a new web API and want the user to be able to specify which fields will be returned to them in the URL.
My current thoughts are:
For an example model:
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; } }
and a url like this
http://example.com/api/values/?_fields=Number,Internal(Something)
will return it
[ { "Number": 0, "Internal": { "Number": 0 } } ]
I came up with a method below to achieve this, but it has some disadvantages. That is, it would not be able to process if Internal was enumerated from ValueInternal or does not support include all or include all except support, or if T and TResult are different types. Does anyone have any suggestions on how I can improve this, or if there is already a way to do this, that I am missing.
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; }
After reading a little more, I think I want to do something like the answer to this LINQ question : Dynamic selection , but still has the same disadvantages as my solution