Last active
December 6, 2018 10:34
-
-
Save gh0stwizard/e2394cf09cd1eac01c1e5a3c22e0d014 to your computer and use it in GitHub Desktop.
LINQExtensionToDynamic.cs: convert queryable<Entity> to queryable<object> at runtime
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
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using Nequeo.Reflection; /* https://github.com/nequeo/misc/blob/master/csharp/DynamicTypeBuilder.cs */ | |
/* | |
This is a proof of concept how create at runtime the code like this: | |
var query = datacontext.Users.Where(x => x.IsActive); | |
var results = query.Select(x => new { Id = x.Id, Name = x.Name }); | |
DoSomething(results); | |
Using the code below the same as above could be written like that: | |
var query = datacontext.Users.Where(x => x.IsActive); | |
var dtb = new DynamicTypeBuilder("DynamicUsers"); | |
var results = query.ToDynamic(dtb, new string[] { "Id", "Name" }); | |
DoSomething(results); | |
Current implementation has limitations: | |
* Nested objects are not created dynamicaly e.g. | |
new { Id = x.Id, Ticket = new { Id = x.Ticket.Id } }; | |
The conversion above at the moment is not supported. | |
* Nested properties will have names without dot ("."): | |
var results = query.ToDynamic(dtb, new string[] { "Id", "User.Name" }); | |
foreach (var o in results) | |
if (o.UserName == "...") { ... } | |
Why? | |
* LINQ for .Net 4.5 does not support queries with Lists, Dictionaries. | |
Previous versions using such a way not working anymore. | |
* LINQ must optimize the result query to reduce workload on database server, | |
like it done at compile time. | |
*/ | |
namespace gh0stwizard | |
{ | |
public static class LinqExtensionToDynamic | |
{ | |
public static IQueryable<object> ToDynamic<T>(this IOrderedQueryable<T> query, DynamicTypeBuilder typeBuilder, IEnumerable<string> fields) | |
{ | |
return ((IQueryable<T>)query).ToDynamic(typeBuilder, fields); | |
} | |
/// <summary> | |
/// Creates at runtime queryable of objects for selected fields. | |
/// </summary> | |
/// <typeparam name="T">Queryable output type</typeparam> | |
/// <param name="query">IQueryable object</param> | |
/// <param name="typeBuilder">DynamicTypeBuilder object</param> | |
/// <param name="fields">List of fields, e.g. "Id", "User.Name" etc.</param> | |
/// <returns>IQueryable of object</returns> | |
public static IQueryable<object> ToDynamic<T>(this IQueryable<T> query, DynamicTypeBuilder typeBuilder, IEnumerable<string> fields) | |
{ | |
var iType = typeof(T); | |
var itemParam = Expression.Parameter(iType, "_"); | |
var members = fields.ToDictionary(k => k.Replace(".", ""), v => CreateExpression(itemParam, v)); | |
var dynaprop = fields.Select(f => new DynamicProperty(f.Replace(".", ""), iType.GetPropertyType(f))); | |
var dynaname = string.Format("{0}{1}", iType.Name, dynaprop.GetHashCode()); | |
var dynatest = typeBuilder.Create(dynaname, dynaprop); | |
var dynatype = dynatest.GetType(); | |
var newExp = Expression.New(dynatype.GetConstructor(new Type[0])); | |
var bindings = members.Select(x => Expression.Bind(dynatype.GetProperty(x.Key), x.Value.Body)); | |
var body = Expression.MemberInit(newExp, bindings); | |
var lambda = Expression.Lambda<Func<T, object>>(body, itemParam); | |
return query.Select(lambda); | |
} | |
private static LambdaExpression CreateExpression(ParameterExpression param, string propertyName) | |
{ | |
Expression body = param; | |
foreach (var member in propertyName.Split('.')) | |
body = Expression.PropertyOrField(body, member); | |
return Expression.Lambda(body, param); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment