Get Expression of property from collection element c# - c#

Here is the problem:
I want to write extension method for getting property expression of collection field.
Here is an example.
I have the following class:
class Foo
{
public int Id {get; set;}
public List<Foo> Collection {get; set;}
}
And I have the following code:
Expression<Func<Foo, object>> expression = x => x.Collection.First().Id;
And I want to create an extension to get the same result expression with following signature:
Expression<Func<Foo, object>> expression = x => x.Collection.Field(foo => foo.Id);
static Func<Foo, object> Field(this List<Foo> people, Expression<Func<Foo, object>> accessor)
{
// implementation goes here
}
The main purpose of it is to generate path to property. In this case we need to get the following result: "Collection.Id". I already have method to convert Expression to property path. The Field method is an extension method, which I want. It should return the same result like x => x.Collection.First().Id but with the following signature: x => x.Collection.Field(foo => foo.Id)
Is there any way to do this?
Thanks a lot.

Since no methods are executed when assigning a lambda to an Expression variable, it isn't possible to have a method as you describe work.
What you can do is create a method that takes in the collection expression and the property expression and creates a new Expression lambda that has the desired result.
Expression<Func<T, TProp>> Field<T, TProp>(Expression<Func<T, IEnumerable<T>>> collectionEFn, Expression<Func<T, TProp>> pathEFn) {
// (Foo x)
var parm = collectionEFn.Parameters[0];
// x.Collection
var collectionExpr = collectionEFn.Body;
// y.Id
var pathExpr = (MemberExpression)pathEFn.Body;
// x.Collection.First()
var firstExpr = Expression.Call(typeof(Enumerable), "First", new[] { typeof(T) }, collectionExpr);
// x.Collection.First().Id
var newBody = Expression.Property(firstExpr, pathExpr.Member.Name);
// (Foo x) => x.Collection.First().Id
return Expression.Lambda<Func<T, TProp>>(newBody, parm);
}
With this method defined, you can call it as so:
var expr = Field((Foo x) => x.Collection, x => x.Id);
and the resulting value for expr will be an Expression tree representing x => x.Collection.First().Id
Note that the resulting type has the result type of the property referenced; if you want object, you would need to add a call to Convert to convert the PropertyExpression to the object type.

If you insist on doing so (like the others I am not still clear about your goal of doing this), you can create a generic extension method that takes a lambda expression as a parameter, and returns an expression. Something like this:
public static class MyExtensions
{
public static Expression<Func<T, object>> Field<T, TPropert>(
this IEnumerable<T> collection,
Expression<Func<T, TPropert>> field)
{
var prm = Expression.Parameter(typeof(IEnumerable<T>), "x");
var first = Expression.Call(typeof(Enumerable), "First", new[] { typeof(T) }, prm);
var body = Expression.Property(first, ((MemberExpression)field.Body).Member.Name);
return Expression.Lambda<Func<T, object>>(body, field.Parameters);
}
}

Related

Get property types from generic T without using reflection

I have a sorting/filtering class that takes an IEnumerable<T> for data and a object that contains the sorting/filtering conditions.
I am building expressions to then execute over the collection.
ParameterExpression parameter = Expression.Parameter(typeof(T));
Expression left = splitFilter.Aggregate((Expression)parameter, Expression.PropertyOrField);
var constant = Expression.Constant(filter.Value.Text.ToLower(), typeof(string));
var body = Expression.Call(MakeString(left, constant), "Contains", Type.EmptyTypes, constant);
Expression bodyNullCheck = CheckForNullableParameters(left, body);
Expression<Func<T, bool>> ex = Expression.Lambda<Func<T, bool>>(bodyNullCheck, parameter);
Example data structure:
public class Customer
{
public int Id {get; set;}
public string Name {get; set;}
public List<StoreItem> BoughtItems {get; set;}
}
public class StoreItem
{
public int Id {get; set;}
public string Name {get; set;}
}
The solution works great for things that have a depth of 1 for example:
Here we can easily retrieve all Customers that have "t" in their name.
The problem comes when we wish to get all customers which have bought an item which contains "Bag" in the name.
From the front-end, I'm currently getting "BoughtItems.Name" but I can't seem to get into the "StoreItem" object even though I have the exact property name.
string[] splitFilter = filter.Key.Split('.'); // Contains {"BoughtItems", "Name"}
Expression parent = new string[] { splitFilter[0] }.Aggregate((Expression)parameter, Expression.PropertyOrField);
Expression child = new string[] { splitFilter[1] }.Aggregate(parent, Expression.PropertyOrField);
Currently, I'm trying to get the expressions for both objects separately and then to somehow combine them, but if I try to get the type of the first expression it says it can't access property Name of type Collection. (duuh)
Is there a way in C# to say "The items inside this Generic collection have this property, now using this property let's go down 1 layer and get another property?" (StoreItem.Name)
The result I'm trying to get should be semi-generic and give the same result as the example below, without using reflection.
string searchBy = "test";
var result = MyCustomers.Where(x => x.BoughtItems.Any(y => y.Name.Contains(searchBy))).ToList()
As a personal preference, I like explicit over implicit. So unless you have an un-maintainable amount of different cases coming from front-end, you should implement each case explicitly.
Let us get back to the question at hand.
You would like to achieve the following expression tree from my understanding:
Expression<Func<Customer, bool>> exp =
myCustomer => myCustomer.BoughtItems.Any(storeItem => storeItem.Name.Contains("test"));
I like commenting my code extensively so from here on most of the time comments will do the explaining :)
var myCustomerParameter = Expression.Parameter(typeof(Customer), "myCustomer");
var storeItemParameter = Expression.Parameter(typeof(StoreItem), "storeItem");
// This expression tree creates the following lambda
// storeItem => storeItem.Name.Contains("test")
var innerLambda = Expression.Lambda<Func<StoreItem, bool>>(
Expression.Call(
// storeItem.Name
Expression.Property(storeItemParameter, "Name"),
// storeItem.Name.Contains
MethodSelectorExtensions.MethodSelector<string>("Contains", new[] {typeof(string)}),
// storeItem.Name.Contains("test")
Expression.Constant("test", typeof(string))
),
storeItemParameter
);
// This expression tree creates the following lambda
// myCustomer => Enumerable.Any(myCustomer.BoughtItems, storeItem => storeItem.Name.Contains("test")
var finalLambda = Expression.Lambda<Func<Customer, bool>>(
Expression.Call(
// Since LINQ is extensions methods, it is static method, no instance is required
null,
// Enumerable.Any
MethodSelectorExtensions.LinqMethodSelector("Any", new[] {typeof(IEnumerable<StoreItem>), typeof(Func<StoreItem, bool>)}),
// Enumerable.Any(myCustomer.BoughtItems, storeItem => storeItem.Name.Contains("test"))
Expression.Property(myCustomerParameter, "BoughtItems"), innerLambda
),
myCustomerParameter
);
Utility methods for creating Expressions
public static class MethodSelectorExtensions {
/// <summary>
/// Cached delegate to be used in typeof(Enumerable) methods
/// </summary>
private static readonly Func<string, Type[], MethodInfo> EnumerableMethodSelector;
/// <summary>
/// Static ctor to cache EnumerableMethodSelector function
/// </summary>
static MethodSelectorExtensions() {
var genericMethodSelector = typeof(MethodSelectorExtensions).GetMethod(nameof(MethodSelector), new[] {typeof(string), typeof(Type[])});
// C# does not allow for static type generic methods but we can create through type system ;)
EnumerableMethodSelector = (Func<string, Type[], MethodInfo>) genericMethodSelector.MakeGenericMethod(typeof(Enumerable)).CreateDelegate(typeof(Func<string, Type[], MethodInfo>));
}
public static MethodInfo MethodSelector<TType>(string nameOfMethod, Type[] functionParameterTypes) {
return typeof(TType)
.GetMethods()
.Where(n => n.Name == nameOfMethod)
// in case we have TType as Enumerable, we will need to extract generic parameter
.Select(n => n.IsGenericMethod ? n.MakeGenericMethod(functionParameterTypes[0].GenericTypeArguments[0]) : n)
.Single(n => n.GetParameters().Select(p => p.ParameterType).SequenceEqual(functionParameterTypes));
}
public static MethodInfo LinqMethodSelector(string nameOfMethod, Type[] functionParameterTypes) {
return EnumerableMethodSelector(nameOfMethod, functionParameterTypes);
}
}
And let us have a final look at the running program:

Using Expression vs Func

I have several autocomplete actions, one of them is listed below. Instead of writing different predicates for each autocomplete Where method, I have created an autoCompletePredicate. Since I have multiple autocompletes I am using Reflection to get the Property which is required for that specific AutoComplete and use that Property in my autoCompletePredicate.
I have following code which is working alright.
static string param1, param2;
static PropertyInfo[] properties;
static PropertyInfo prop1, prop2;
public IHttpActionResult GetAutComplete(string term, string dependent)
{
int pagerSize = 10;
properties = new MyObject().GetType().GetProperties();
prop1 = properties.Where(p => p.Name.ToUpper().Equals("PROP1")).FirstOrDefault();
prop2 = properties.Where(p => p.Name.ToUpper().Equals("PROP2")).FirstOrDefault();
param1 = term;
param2 = dependent;
return Json(context.MyObject.Where(autoCompletePredicate).Select(r => new { label = r.PROP1 }).Distinct().OrderBy(r => r.label).Take(pagerSize).ToList());
}
Func<MyObject, int, bool> autoCompletePredicate = (GF, index) =>
{
bool isFound = false;
string term, dependent;
term = prop1.GetValue(GF).ToString();
dependent = prop2.GetValue(GF).ToString();
var termFound = term.Contains(param1.ToUpper());
var dependentFound = String.IsNullOrEmpty(param2) ? true : dependent.Contains(param2.ToUpper());
isFound = termFound && dependentFound;
return isFound;
};
How can I change this code into Expression. I tried below code which compiled fine but at runtime I got the following error
public static Expression<Func<MyObject, bool>> AutoCompleteExpression()
{
return r => prop1.GetValue(r).ToString().Contains(param1.ToUpper()) && (String.IsNullOrEmpty(param2) ? true : prop2.GetValue(r).ToString().Contains(param2.ToUpper()));
}
"LINQ to Entities does not recognize the method 'System.Object
GetValue(System.Object)' method, and this method cannot be translated
into a store expression."
I looked at the following post which makes absolute sense, but I am not sure how I can use that example in my scenario (which is dynamically finding properties using Reflection).
Also, what I would like to know what can be advantage of using Expression vs Func (specially in my case)
You are trying to trying to execute Contains method on string and want that to be represented in the ExpressionTrees, following is the code you need:
Create String Extension method - Contains: (Case Insensitive)
public static class StringExtensions
{
public static bool Contains(this string source, string toCheck)
{
return source.IndexOf(toCheck, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
Create the AutoCompleteExpression method as follows, it returns Func<MyObject, bool>:
public static Func<MyObject, bool> AutoCompleteExpression()
{
// Create ParameterExpression
ParameterExpression parameterType = Expression.Parameter(typeof(MyObject), "object");
// Create MemberExpression for Columns
MemberExpression typeColumnProp1 = Expression.Property(parameterType, "PROP1");
MemberExpression typeColumnProp2 = Expression.Property(parameterType, "PROP2");
// Create MethoIndo
MethodInfo containsMethodInfo = typeof(StringExtensions).GetMethod("Contains",new[] { typeof(string), typeof(string) },null);
// Create ConstantExpression values
ConstantExpression constant1 = Expression.Constant(param1, typeof(string));
ConstantExpression constant2 = Expression.Constant(param2, typeof(string));
// Expression for calling methods
MethodCallExpression expression1 = Expression.Call(null, containsMethodInfo, typeColumnProp1, constant1);
MethodCallExpression expression2 = Expression.Call(null, containsMethodInfo, typeColumnProp2, constant2);
// Combine `MethodCallExpression` to create Binary Expression
BinaryExpression resultExpression = Expression.And(expression1,expression2);
// Compile Expression tree to fetch `Func<MyObject, bool>`
return Expression.Lambda<Func<MyObject, bool>>(resultExpression, parameterType).Compile();
}
It is possible to add lot more flexibility by defining custom extension methods and combining expressions using And / Or

How to assign a value from MemberExpression to a field according to another MemberExpression?

I would like write a method which accepts two MemberExpression, and generates a delegate which accepts two objects - source and target, and assigns the value from the source - according to it's MemberExpression, to the field of the target, according to the second MemberExpression. The objects does not have to be of the same type.
I'm looking for something like this:
public Action<TSource, TTarget> Map(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var sourceField = getter.Body as MemberExpression;
var targetField = setter.Body as MemberExpression;
/*
* Now I would like to create a lambda expression which accepts TSource and TTarget instances,
* and assings TTarget according to the above getter and setter expressions. Kind of like:
* var assignExp = Expression.Assign(x, y);
* var lambda = Expression.Lambda<Action<TTarget, TSource>>( .... ).Compile();
* return lambda;
*/
}
Usage:
Target target;
Source source;
//...
var action = Map(p => p.NestedField.Dummy, x => x.TargetName);
action(source, target);
I don't understand how to build the expressions to send to Expression.Assign.
At this point, I don't mind about null values or initialization of fields. Please assume all fields are initialized.
Assign is used to generate assign expression, but in your case each lambda expression has own parameter, and both this parameters should be send to a new lambda expression.
So in my example i generate new assign expression, then create a new lambda expression, and send ParameterExpression from both getter and setter expressions to a new lambda.
So it should be like this:
Here is working sample - https://dotnetfiddle.net/uuPVAl and the code itself
using System;
using System.Linq.Expressions;
public class Program
{
public static void Main(string[] args)
{
Target target = new Target();
Source source = new Source()
{
NestedField = new NestedSource()
{
Dummy = "Hello world"
}
};
var action = Map<Source, Target>(p => p.NestedField.Dummy, x => x.TargetName);
action(source, target);
Console.WriteLine(target.TargetName);
}
public static Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var sourceField = getter.Body as MemberExpression;
var targetField = setter.Body as MemberExpression;
// here we create new assign expression
var assign = Expression.Assign(targetField, sourceField);
// and then compile it with original two parameters
var lambda = Expression.Lambda<Action<TSource, TTarget>>(assign, getter.Parameters[0], setter.Parameters[0]);
return lambda.Compile();
}
}
public class Target
{
public string TargetName { get; set; }
}
public class NestedSource
{
public string Dummy { get; set; }
}
public class Source
{
public NestedSource NestedField { get; set; }
}
UPDATE
So each Lambda Expression can have any parameters. From code side it's ParameterExpression. When you write expression as typical code, then it means function parameters, so in your case (p) => p.NestedField.Dummy - (p) is parameter of that function. And expression inside body uses it - p.NestedField.Dummy, so to be able to compile it - lambda expression needs to know that parameter.
In this case you have two lambda expressions for target and source, and each of them have own parameter - (p) and (x) and each expression use own parameter. But in result function we need to use both of them, as we have two parameters in the function, so we need to resend original ParameterExpression from source and target to a new lambda. Or you can create a new ParameterExpression but then you need to create a new tree as old one will use old ParameterExpression. Usually such things are done with ExpressionVisitor class, but in your case we can just resend original expressions without tree body changes.
This will do:
public Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var targetPropertyExpression = setter.Body as MemberExpression;
var targetProperty = targetPropertyExpression.Member as PropertyInfo;
return (src, tgt) => { targetProperty.SetValue(tgt, getter.Compile().Invoke(src)); };
}
It get's the property of the setter from the 1st lambda expression and just returns an action, which assigns the value to the property based on the 2nd lambda expression, which just needs to be invoked.
Take care of the <TSource, object> though, you maybe need an additional cast.

Set only second argument type in generic method

I have to create a method for selecting a firts property from collection with the specified type.
I have created the method like this (I have removed some parts for the brevity):
public static IQueryable<TResult> SelectFirstPropertyWithType<T, TResult>(this IQueryable<T> source)
{
// Get the first property which has the TResult type
var propertyName = typeof(T).GetProperties()
.Where(x => x.PropertyType == typeof(TResult))
.Select(x => x.Name)
.FirstOrDefault();
var parameter = Expression.Parameter(typeof(T));
var body = Expression.Convert(Expression.PropertyOrField(parameter, propertyName), typeof(TResult));
var expression = Expression.Lambda<Func<T, TResult>>(body, parameter);
return source.Select(expression);
}
And I can call this method as:
List<Person> personList = new List<Person>();
// .. initialize personList
personList.AsQueryable()
.SelectFirstPropertyWithType<Person, int>()
.ToList();
Everything works fine.
But, I don't want to set first argument type as Person, because compiler can infer this argument type fromthe source of the collection. Is there any way to call the method like that:
.SelectFirstPropertyWithType<int>()
The problem is I need T parameter inside my method, and I don't want to create Func with reflection in runtime.
Thanks.
No. The compiler should be able to infer all type parameters. If it can't it will demand you to specify all of them.
The compiler can't tell you it can infer the first or the second, so instead of having a non deterministic compiling application, it just breaks.
C# Generics simply does not allow you to specify a subset of the type parameters. It's all or nothing.
The way to work around this is to write a fluent interface. You break this operation up into a chain of methods.
public class FirstPropertyWithTypeSelector<T>
{
private readonly IQueryable<T> _source;
public FirstPropertyWithTypeSelector(IQueryable<T> source)
{
_source = source;
}
public IQueryable<TResult> OfType<TResult>()
{
// Get the first property which has the TResult type
var propertyName = typeof(T).GetProperties()
.Where(x => x.PropertyType == typeof(TResult))
.Select(x => x.Name)
.FirstOrDefault();
var parameter = Expression.Parameter(typeof(T));
var body = Expression.Convert(Expression.PropertyOrField(parameter, propertyName), typeof(TResult));
var expression = Expression.Lambda<Func<T, TResult>>(body, parameter);
return _source.Select(expression);
}
}
public static FirstPropertyWithTypeSelector<T> SelectFirstProperty(this IQueryable<T> source)
{
return new FirstPropertyWithTypeSelector<T>(source);
}
Now you can call:
personList.AsQueryable()
.SelectFirstProperty().OfType<int>()
.ToList();

Creating a dynamic Linq select clause from Expressions

Let's say I have defined the following variables:
IQueryable<MyClass> myQueryable;
Dictionary<string, Expression<Func<MyClass, bool>>> extraFields;
// the dictionary is keyed by a field name
Now, I want to tack on some dynamic fields to the IQueryable, so that it returns an IQueryable<ExtendedMyClass>, where ExtendedMyClass is defined as:
class ExtendedMyClass
{
public MyClass MyObject {get; set;}
public IEnumerable<StringAndBool> ExtraFieldValues {get; set;}
}
class StringAndBool
{
public string FieldName {get; set;}
public bool IsTrue {get; set;}
}
In other words, for every value in extraFields, I want to have a value in ExtendedMyClass.ExtraFieldValues representing whether that expression evaluates to True or not for that row.
I have a feeling this should be doable in dynamic Linq and LinqKit, though I've never used that seriously before. I'm also open to other suggestions, especially if this can be done in good ol' strong-typed Linq.
I am using Linq to Entities, so the query needs to translate to SQL.
So, we'll have a lot of steps here, but each individual step should be fairly short, self-contained, reusable, and relatively understandable.
The first thing we'll do is create a method that can combine expressions. What it will do is take an expression that accepts some input and generates an intermediate value. Then it will take a second expression that accepts, as input, the same input as the first, the type of the intermediate result, and then computes a new result. It will return a new expression taking the input of the first, and returning the output of the second.
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
To do this we simply replace all instances of the second parameter in the second expression's body with the body of the first expression. We also need to ensure both implementations use the same parameter instance for the main parameter.
This implementation requires having a method to replace all instances of one expression with another:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
Next we'll write a method that accepts a sequences of expressions that accept the same input and compute the same type of output. It will transform this into a single expression that accepts the same input, but computes a sequence of the output as a result, in which each item in the sequence represents the result of each of the input expressions.
This implementation is fairly straightforward; we create a new array, use the body of each expression (replacing the parameters with a consistent one) as each item in the array.
public static Expression<Func<T, IEnumerable<TResult>>> AsSequence<T, TResult>(
this IEnumerable<Expression<Func<T, TResult>>> expressions)
{
var param = Expression.Parameter(typeof(T));
var body = Expression.NewArrayInit(typeof(TResult),
expressions.Select(selector =>
selector.Body.Replace(selector.Parameters[0], param)));
return Expression.Lambda<Func<T, IEnumerable<TResult>>>(body, param);
}
Now that we have all of these general purpose helper methods out of the way, we can start working on your specific situation.
The first step here is to turn your dictionary into a sequence of expressions, each accepting a MyClass and creating a StringAndBool that represents that pair. To do this we'll use Combine on the value of the dictionary, and then use a lambda as the second expression to use it's intermediate result to compute a StringAndBool object, in addition to closing over the pair's key.
IEnumerable<Expression<Func<MyClass, StringAndBool>>> stringAndBools =
extraFields.Select(pair => pair.Value.Combine((foo, isTrue) =>
new StringAndBool()
{
FieldName = pair.Key,
IsTrue = isTrue
}));
Now we can use our AsSequence method to transform this from a sequence of selectors into a single selector that selects out a sequence:
Expression<Func<MyClass, IEnumerable<StringAndBool>>> extrafieldsSelector =
stringAndBools.AsSequence();
Now we're almost done. We now just need to use Combine on this expression to write out our lambda for selecting a MyClass into an ExtendedMyClass while using the previous generated selector for selecting out the extra fields:
var finalQuery = myQueryable.Select(
extrafieldsSelector.Combine((foo, extraFieldValues) =>
new ExtendedMyClass
{
MyObject = foo,
ExtraFieldValues = extraFieldValues,
}));
We can take this same code, remove the intermediate variable and rely on type inference to pull it down to a single statement, assuming you don't find it too unweidly:
var finalQuery = myQueryable.Select(extraFields
.Select(pair => pair.Value.Combine((foo, isTrue) =>
new StringAndBool()
{
FieldName = pair.Key,
IsTrue = isTrue
}))
.AsSequence()
.Combine((foo, extraFieldValues) =>
new ExtendedMyClass
{
MyObject = foo,
ExtraFieldValues = extraFieldValues,
}));
It's worth noting that a key advantage of this general approach is that the use of the higher level Expression methods results in code that is at least reasonably understandable, but also that can be statically verified, at compile time, to be type safe. There are a handful of general purpose, reusable, testable, verifiable, extension methods here that, once written, allows us to solve the problem purely through composition of methods and lambdas, and that doesn't require any actual expression manipulation, which is both complex, error prone, and removes all type safety. Each of these extension methods is designed in such a way that the resulting expression will always be valid, so long as the input expressions are valid, and the input expressions here are all known to be valid as they are lambda expressions, which the compiler verifies for type safety.
I think it's helpful here to take an example extraFields, imagine how would the expression that you need look like and then figure out how to actually create it.
So, if you have:
var extraFields = new Dictionary<string, Expression<Func<MyClass, bool>>>
{
{ "Foo", x => x.Foo },
{ "Bar", x => x.Bar }
};
Then you want to generate something like:
myQueryable.Select(
x => new ExtendedMyClass
{
MyObject = x,
ExtraFieldValues =
new[]
{
new StringAndBool { FieldName = "Foo", IsTrue = x.Foo },
new StringAndBool { FieldName = "Bar", IsTrue = x.Bar }
}
});
Now you can use the expression trees API and LINQKit to create this expression:
public static IQueryable<ExtendedMyClass> Extend(
IQueryable<MyClass> myQueryable,
Dictionary<string, Expression<Func<MyClass, bool>>> extraFields)
{
Func<Expression<Func<MyClass, bool>>, MyClass, bool> invoke =
LinqKit.Extensions.Invoke;
var parameter = Expression.Parameter(typeof(MyClass));
var extraFieldsExpression =
Expression.Lambda<Func<MyClass, StringAndBool[]>>(
Expression.NewArrayInit(
typeof(StringAndBool),
extraFields.Select(
field => Expression.MemberInit(
Expression.New(typeof(StringAndBool)),
new MemberBinding[]
{
Expression.Bind(
typeof(StringAndBool).GetProperty("FieldName"),
Expression.Constant(field.Key)),
Expression.Bind(
typeof(StringAndBool).GetProperty("IsTrue"),
Expression.Call(
invoke.Method,
Expression.Constant(field.Value),
parameter))
}))),
parameter);
Expression<Func<MyClass, ExtendedMyClass>> selectExpression =
x => new ExtendedMyClass
{
MyObject = x,
ExtraFieldValues = extraFieldsExpression.Invoke(x)
};
return myQueryable.Select(selectExpression.Expand());
}

Categories