Last active
December 6, 2018 10:39
-
-
Save charlessolar/20f453de023ff75edeb8 to your computer and use it in GitHub Desktop.
Building objects from a list of property names for Dynamic DTOs - useful for authorization field level filtering and situations when you don't know the type of data a service will return
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static IQueryable<dynamic> ToDynamic<T>(this IQueryable<T> query, ISet<String> fields) | |
{ | |
var pocoType = typeof(T); | |
var itemParam = Expression.Parameter(pocoType, "x"); | |
var members = fields.Select(f => Expression.PropertyOrField(itemParam, f)); | |
var addMethod = typeof(IDictionary<string, object>).GetMethod( | |
"Add", new Type[] { typeof(string), typeof(object) }); | |
var elementInits = members.Select(m => Expression.ElementInit(addMethod, Expression.Constant(m.Member.Name), Expression.Convert(m, typeof(Object)))); | |
var expando = Expression.New(typeof(ExpandoObject)); | |
var lambda = Expression.Lambda<Expression<Func<T, dynamic>>>(Expression.ListInit(expando, elementInits), itemParam); | |
return query.Select(lambda.Compile()); | |
} | |
public static IQueryable<T> FromDynamic<T>(this IQueryable<dynamic> query) where T : class, new() | |
{ | |
var itemParam = Expression.Parameter(typeof(ExpandoObject), "x"); | |
var members = typeof(T).GetProperties().Where(p => p.CanWrite).Select(f => Expression.Property(itemParam, f)); | |
var selector = Expression.MemberInit(Expression.New(typeof(T)), | |
members.Select(m => Expression.Bind(typeof(T).GetMember(m.Member.Name).Single(), m)) | |
); | |
var lambda = Expression.Lambda<Expression<Func<dynamic, T>>>(selector, itemParam); | |
return query.Select(lambda.Compile()); | |
} | |
public static T FromDynamic<T>(this IDictionary<string, object> dictionary) where T : class, new() | |
{ | |
var bindings = new List<MemberBinding>(); | |
foreach (var sourceProperty in typeof(T).GetProperties().Where(x => x.CanWrite)) | |
{ | |
var key = dictionary.Keys.SingleOrDefault(x => x.Equals(sourceProperty.Name, StringComparison.OrdinalIgnoreCase)); | |
if (string.IsNullOrEmpty(key)) continue; | |
var propertyValue = dictionary[key]; | |
bindings.Add(Expression.Bind(sourceProperty, Expression.Constant(propertyValue))); | |
} | |
Expression memberInit = Expression.MemberInit(Expression.New(typeof(T)), bindings); | |
return Expression.Lambda<Func<T>>(memberInit).Compile().Invoke(); | |
} | |
public static dynamic ToDynamic<T>(this T obj, ISet<String> fields) | |
{ | |
IDictionary<string, object> expando = new ExpandoObject(); | |
var pocoType = typeof(T); | |
foreach (var f in fields) | |
{ | |
var propertyExpression = Expression.Property(Expression.Constant(obj), pocoType.GetProperty(f)); | |
var currentValue = Expression.Lambda<Func<string>>(propertyExpression).Compile().Invoke(); | |
expando.Add(f, currentValue); | |
} | |
return expando as ExpandoObject; | |
} |
@cmalcom line 15 should be chamged to var lambda = Expression.Lambda<Func<T, dynamic>>(Expression.ListInit(expando, elementInits), itemParam)
lambda is now of type Expression<Func<T, dynamic>>
and final on line 17 return query.Select(lambda.Compile());
you can leave out the call to Compile() return query.Select(lambda);
I was unable to get it work with .Net 4.5. Another way: https://gist.github.com/gh0stwizard/e2394cf09cd1eac01c1e5a3c22e0d014
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@cmalcom Sorry gists don't seem to send notifications when comments come in...
This code is from 2014 or 2015 - when C# 5.0 just came out - as an example of some cool dynamic expression compiling.
This is still the best way to create dynamic objects based on a filtered list of fields, but I'm afraid I can't help much with errors I no longer work on the project where this was needed. Are you targeting 4.5? They could have changed something after I wrote this to break something. From the error you are seeing it seems there's a missing Expression.Cast - take a look at dynamic lambda compiling in C#. There's not a lot written about it but it can help