Skip to content

Instantly share code, notes, and snippets.

@charlessolar
Last active December 6, 2018 10:39
Show Gist options
  • Save charlessolar/20f453de023ff75edeb8 to your computer and use it in GitHub Desktop.
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
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;
}
@charlessolar
Copy link
Author

@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

@rubenknuijver
Copy link

@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);

@gh0stwizard
Copy link

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