Last active
January 26, 2021 19:09
-
-
Save afruzan/a46b528a145b59cc1c0d434701b5880c to your computer and use it in GitHub Desktop.
Useful EF6/EfCore LINQ Query Extensions
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 class EFLinqExtensions | |
{ | |
public static IQueryable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this IQueryable<TOuter> outer, IQueryable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<JoinTuple<TOuter, TInner>, TResult>> resultSelector) | |
{ | |
return outer.GroupJoin(inner, outerKeySelector, innerKeySelector, (b, @is) => new { b, @is }).SelectMany(i => [email protected](), (b, i) => new JoinTuple<TOuter, TInner> { Outer = b.b, Inner = i }).Select(resultSelector); | |
} | |
public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<JoinTuple<TOuter, TInner>, TResult> resultSelector) | |
{ | |
return outer.GroupJoin(inner, outerKeySelector, innerKeySelector, (b, @is) => new { b, @is }).SelectMany(i => [email protected](), (b, i) => new JoinTuple<TOuter, TInner> { Outer = b.b, Inner = i }).Select(resultSelector); | |
} | |
public static IQueryable<TResult> SelectDynamic<TSource, TResult>(this IQueryable<TSource> source, TResult resultType, IDictionary<string, string> keyPropertyNamesMap) | |
{ | |
return source.Select(lambda_SelectProps<TSource, TResult>(keyPropertyNamesMap)); | |
} | |
public static IQueryable<IGrouping<TKey, TElement>> GroupByDynamic<TElement, TKey>(this IQueryable<TElement> source, TKey keyType, IDictionary<string, string> keyPropertyNamesMap) | |
{ | |
return source.GroupBy(lambda_SelectProps<TElement, TKey>(keyPropertyNamesMap)); | |
} | |
public static IOrderedQueryable<TSource> OrderByDynamic<TSource>(this IQueryable<TSource> source, IEnumerable<string> sourcePropertyNames) | |
{ | |
return source.OrderBy(lambda_SelectPropsAsTuple<TSource>(sourcePropertyNames)); | |
} | |
public static IOrderedQueryable<TSource> OrderByDescendingDynamic<TSource>(this IQueryable<TSource> source, IEnumerable<string> sourcePropertyNames) | |
{ | |
return source.OrderByDescending(lambda_SelectPropsAsTuple<TSource>(sourcePropertyNames)); | |
} | |
private static Expression<Func<TSource, TResult>> lambda_SelectProps<TSource, TResult>(IDictionary<string, string> keyPropertyNamesMap) | |
{ | |
var item = Expression.Parameter(typeof(TSource), "item"); | |
var init = Expression.MemberInit(Expression.New(typeof(TResult)), keyPropertyNamesMap.Select((prop, j) => | |
{ | |
var names = prop.Value.Split('.'); | |
var getter = Expression.Property(item, names[0]); | |
for (int i = 1; i < names.Length; i++) | |
{ | |
getter = Expression.Property(getter, names[i]); | |
} | |
var setter = typeof(TResult).GetProperty(prop.Key); | |
if (setter == null) | |
{ | |
throw new ArgumentException("property '" + prop.Key + "' was not found in " + typeof(TResult).Name); | |
} | |
return Expression.Bind(setter, getter); | |
})); | |
return Expression.Lambda<Func<TSource, TResult>>(init, item); | |
} | |
private static Expression<Func<TSource, object>> lambda_SelectPropsAsTuple<TSource>(IEnumerable<string> propertyNames) | |
{ | |
var item = Expression.Parameter(typeof(TSource), "item"); | |
var properties = propertyNames.Select(prop => | |
{ | |
var names = prop.Split('.'); | |
var getter = Expression.Property(item, names[0]); | |
for (int i = 1; i < names.Length; i++) | |
{ | |
getter = Expression.Property(getter, names[i]); | |
} | |
return getter; | |
}).ToArray(); | |
if (properties.Length > 15) | |
{ | |
throw new ArgumentOutOfRangeException("propertyNames max length is 15."); | |
} | |
var propertyTypes = Enumerable.Repeat(typeof(object), 15).ToArray(); | |
var providedTypes = properties.Select(p => p.Type).ToArray(); | |
Array.Copy(providedTypes, propertyTypes, providedTypes.Length); | |
var tupleType = typeof(QueryableTuple<,,,,,,,,,,,,,,>).MakeGenericType(propertyTypes); | |
var init = Expression.MemberInit(Expression.New(tupleType), properties.Select((getter, j) => | |
{ | |
var setter = tupleType.GetProperty("Prop" + (j + 1)); | |
if (setter == null) | |
{ | |
throw new ArgumentException("property '" + ("Prop" + (j + 1)) + "' was not found in " + tupleType.Name); | |
} | |
return Expression.Bind(setter, getter); | |
})); | |
return Expression.Lambda<Func<TSource, object>>(init, item); | |
} | |
private class QueryableTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> | |
{ | |
public T1 Prop1 { get; set; } | |
public T2 Prop2 { get; set; } | |
public T3 Prop3 { get; set; } | |
public T4 Prop4 { get; set; } | |
public T5 Prop5 { get; set; } | |
public T6 Prop6 { get; set; } | |
public T7 Prop7 { get; set; } | |
public T8 Prop8 { get; set; } | |
public T9 Prop9 { get; set; } | |
public T10 Prop10 { get; set; } | |
public T11 Prop11 { get; set; } | |
public T12 Prop12 { get; set; } | |
public T13 Prop13 { get; set; } | |
public T14 Prop14 { get; set; } | |
public T15 Prop15 { get; set; } | |
} | |
public static Func<TSource, string> GenerateConcatStringsFunc<TSource>(this IEnumerable<TSource> source, IEnumerable<string> propertyNames, string separator = null, string nullInsteadValue = null) | |
{ | |
return lambda_ConcatStrings<TSource>(propertyNames, separator, nullInsteadValue).Compile(); | |
} | |
public static Func<TSource, decimal> GenerateAddDecimalsFunc<TSource>(this IEnumerable<TSource> source, IEnumerable<string> propertyNames) | |
{ | |
return lambda_AddDecimals<TSource>(propertyNames).Compile(); | |
} | |
private static Expression<Func<TSource, string>> lambda_ConcatStrings<TSource>(IEnumerable<string> propertyNames, string separator = null, string nullInsteadValue = null) | |
{ | |
var item = Expression.Parameter(typeof(TSource), "item"); | |
var properties = propertyNames.Select(prop => | |
{ | |
var names = prop.Split('.'); | |
var getter = Expression.Property(item, names[0]); | |
for (int i = 1; i < names.Length; i++) | |
{ | |
getter = Expression.Property(getter, names[i]); | |
} | |
return getter; | |
}); | |
var joinMethod = typeof(string).GetMethod("Join", new[] { typeof(string), typeof(object[]) }); | |
Expression result = Expression.Call(joinMethod, Expression.Constant(separator ?? ""), Expression.NewArrayInit(typeof(object), properties.Select(g => Expression.Coalesce(Expression.Convert(g, typeof(object)), Expression.Constant(nullInsteadValue))))); | |
return Expression.Lambda<Func<TSource, string>>(result, item); | |
} | |
private static Expression<Func<TSource, decimal>> lambda_AddDecimals<TSource>(IEnumerable<string> propertyNames) | |
{ | |
var item = Expression.Parameter(typeof(TSource), "item"); | |
var properties = propertyNames.Select(prop => | |
{ | |
var names = prop.Split('.'); | |
var getter = Expression.Property(item, names[0]); | |
for (int i = 1; i < names.Length; i++) | |
{ | |
getter = Expression.Property(getter, names[i]); | |
} | |
return getter; | |
}).ToArray(); | |
Expression result = properties[0]; | |
for (int i = 1; i < properties.Length; i++) | |
{ | |
result = Expression.Add(result, properties[i]); | |
} | |
return Expression.Lambda<Func<TSource, decimal>>(result, item); | |
} | |
} | |
public class JoinTuple<TOuter, TInner> | |
{ | |
public TOuter Outer { get; set; } | |
public TInner Inner { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment