I am building an advanced search screen and am using nHibernate to query the DB. I have already built my DataAccess layer and have built a generic method which works great - I pass in an expression to be used as a predicate and passes back a collection of an object that matches the predicate:
public object LoadByPredicate<T>(Expression<Func<T, bool>> predicate) where T : class
Example usage is:
var items = _query.LoadByPredicate<StaticTypes>(x => x.StaticName == type) as List<StaticTypes>;
This is great.
I am now building an advanced search screen on my application where a user can search by between one and 20 different options to return matching "Products". I.e. The Product class has 20 properties and they can search by a combination of Name, Reference, Description, Value etc.....
I'd like to build a mechanism that will construct an expression for me that I can pass into my "LoadByPredicate" method above.
So far I proved this will work:
var type = typeof(Model.Product);
var property = type.GetProperty("ProductMetalProductType");
var parameter = Expression.Parameter(typeof(Model.Product), "x");
Expression<Func<Model.Product, bool>> predicate =
(Expression<Func<Model.Product, bool>>)Expression.Lambda(
Expression.Equal(
Expression.MakeMemberAccess(parameter, property),
Expression.Constant(metalProdType)),
parameter);
This works great for one item the property called "ProductMetalProductType". However I can't see how I can expand this without writing a HUGE amount of code. How can I write some code where I say on the lines of "if Product Metal type is not empty" add an extra expression to refine the search predicate by that?
Or am I going down the wrong track with the way I am constructing my expression?
Thanks in advance
You can do like this:
public class Program
{
static void Main(string[] args)
{
var parameters = new Dictionary<string, object>();
parameters.Add("ProductMetalProductType", 1);
parameters.Add("IsActive", true);
parameters.Add("Name", "New cool product");
var expr = GenerateExpression<Product>(parameters);
Console.WriteLine("Result expression:");
Console.WriteLine(expr.ToString());
}
private static Expression<Func<T, bool>> GenerateExpression<T>(Dictionary<string, object> properties)
{
var type = typeof(T);
List<Expression> expressions = new List<Expression>();
var parameter = Expression.Parameter(typeof(T), "x");
foreach (var key in properties.Keys)
{
var val = properties[key];
var property = type.GetProperty(key);
var eqExpr = Expression.Equal(Expression.MakeMemberAccess(parameter, property), Expression.Constant(val));
expressions.Add(eqExpr);
}
Expression final = expressions.First();
foreach (var expression in expressions.Skip(1))
{
final = Expression.And(final, expression);
}
Expression<Func<T, bool>> predicate =
(Expression<Func<T, bool>>) Expression.Lambda(final, parameter);
return predicate;
}
}
public class Product
{
public int ProductMetalProductType { get; set; }
public bool IsActive { get; set; }
public string Name { get; set; }
}
Here is working fiddle - http://dotnetfiddle.net/t0a9yA
Basically you can fill Dictionary with needed parameters, then generate expression based on that dictionary. Dictionary key is a property name, and value is value for filtering.
Related
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:
I have a class list of records, so user can select to group rows dynamically by property name. For example MenuText, RoleName or ActionName. Then I have to execute grouping so I need a generic method to handle grouping by passing column name.
Example :
public class Menu
{
public string MenuText {get;set;}
public string RoleName {get;set;}
public string ActionName {get;set;}
}
public class Menus
{
var list = new List<Menu>();
list.Add( new Menu {MenuText="abc",RoleName ="Admin", ActionName="xyz"};
list.Add( new Menu {MenuText="abc",RoleName ="Admin", ActionName="xyz"};
list.Add( new Menu {MenuText="abc1",RoleName ="Admin1", ActionName="xyz1"};
list.Add( new Menu {MenuText="abc1",RoleName ="Admin1", ActionName="xyz1"};
/// columnName :- Name of the Menu class ( MenuText or RoleName or ActionName)
public IEnumerable<IGrouping<string,IEnumerable<Menu>>> GroupMenu(string columnName)
{
// Here I want to get group the list of menu by the columnName
}
}
If you're not working with a database you can use Reflection:
private static object GetPropertyValue(object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
}
Used as:
var grouped = enumeration.GroupBy(x => GetPropertyValue(x, columnName));
This is a pretty raw solution, a better way should be to use Dynamic LINQ:
var grouped = enumeration.GroupBy(columnName, selector);
EDIT Dynamic LINQ maybe needs some explanations. It's not a technology, a library or a brand new framework. It's just a convenient name for a couple (2000 LOC) of helpers methods that let you write such queries. Just download their source (if you don't have VS samples installed) and use them in your code.
The following approach would work with LINQ to Objects as well as with LINQ to EF / NHibernate / etc.
It creates an expression that corresponds to the column / property passed as the string and passes that expression to GroupBy:
private static Expression<Func<Menu, string>> GetGroupKey(string property)
{
var parameter = Expression.Parameter(typeof(Menu));
var body = Expression.Property(parameter, property);
return Expression.Lambda<Func<Menu, string>>(body, parameter);
}
Usage with IQueryable<T> based data sources:
context.Menus.GroupBy(GetGroupKey(columnName));
Usage with IEnumerable<T> based data sources:
list.GroupBy(GetGroupKey(columnName).Compile());
BTW: The return type of your method should be IEnumerable<IGrouping<string, Menu>>, because an IGrouping<string, Menu> already means that there can be multiple Menu instances per key.
Simplest way:
if(columnName == "MextText")
{
return list.GroupBy(x => x.MenuText);
}
if(columnName == "RoleName")
{
return list.GroupBy(x => x.RoleName);
}
if(columnName == "ActionName")
{
return list.GroupBy(x => x.ActionName);
}
return list.GroupBy(x => x.MenuText);
You could also use expression trees.
private static Expression<Func<Menu, string>> GetColumnName(string property)
{
var menu = Expression.Parameter(typeof(Menu), "menu");
var menuProperty = Expression.PropertyOrField(menu, property);
var lambda = Expression.Lambda<Func<Menu, string>>(menuProperty, menu);
return lambda;
}
return list.GroupBy(GetColumnName(columnName).Compile());
This will produce a lambda menu => menu.<PropertyName>.
But there's not really much of a difference until the class gets bloated.
I have done this with Dynamic Linq as suggested by Adriano
public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(
this IEnumerable<TElement> elements, params string[] groupSelectors)
{
var selectors = new List<Func<TElement, object>>(groupSelectors.Length);
selectors.AddRange(groupSelectors.Select(selector => DynamicExpression.ParseLambda(typeof (TElement), typeof (object), selector)).Select(l => (Func<TElement, object>) l.Compile()));
return elements.GroupByMany(selectors.ToArray());
}
public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(
this IEnumerable<TElement> elements, params Func<TElement, object>[] groupSelectors)
{
if (groupSelectors.Length > 0)
{
Func<TElement, object> selector = groupSelectors.First();
return elements.GroupBy(selector);
}
return null;
}
You solution is simple to implement for any model, I have just made it generic one.
public static Expression<Func<TElement, string>> GetColumnName<TElement>(string property)
{
var menu = Expression.Parameter(typeof(TElement), "groupCol");
var menuProperty = Expression.PropertyOrField(menu, property);
var lambda = Expression.Lambda<Func<TElement, string>>(menuProperty, menu);
return lambda;
}
so called this as below
_unitOfWork.MenuRepository.Get().GroupBy(LinqExtensions.GetColumnName<Menu>("MenuText").Compile());
Thank you very much for the help.
I want to be able to build up an expression dynamically, which is essentially a property selector.
I am trying to use this so I can provide a flexible search UI and then translate the selected search parameters to an Entity Framework query.
I have most of what I need thanks to another library I am using, but am missing the final part which translates my query string parameters to the appropriate expression selector the other library requires.
The library takes an argument of :
Expression<Func<TObject, TPropertyType>>
An example of how this would be coded if baked into an application would be :
Expression<Func<MyObject, int>> expression = x=> x.IntegerProperty;
However, I need to be able to generate this expression dynamically, as the important point is that all I will know is the type of object (MyObject) and the property name as a string value ("IntegerProperty"). The property value will obviously map to an property on the object which could be of any non complex type.
So essentially I think I am wanting to find a way to build up the expression dynamically which specifies the correct object property to return and where the return value is determined by that property type.
psuedo code :
string ObjectPropertyName
Type ObjectType
Type ObjectPropertyType = typeof(ObjectType).GetProperty(ObjectPropertyName).Property
Expression<Func<[ObjectType], [ObjectPropertyType]>> expression = x=> x.[ObjectPropertyName];
Update :
I have got as far as this
ParameterExpression objectParameter = Expression.Parameter(type, "x");
MemberExpression objectProperty = Expression.Property(objectParameter, "PropertyNameString");
Expression<Func<ObjectType, int>> expression = Expression.Lambda<Func<ObjectType, int>>(objectProperty, objectParameter);
But the problem I have with this is that the return type is not always an int but may be some other type.
Doing what you asked is bit tricky but not impossible. Since the property type is not known until run time so you can not declare the Expression<Func<,>> so it would be done by reflection.
public static class QueryableExtension
{
public static object Build<Tobject>(this Tobject source, string propertyName)
{
var propInfo = typeof(Tobject).GetProperty(propertyName);
var parameter = Expression.Parameter(typeof(Tobject), "x");
var property = Expression.Property(parameter, propInfo);
var delegateType = typeof(Func<,>)
.MakeGenericType(typeof(Tobject), propInfo.PropertyType);
var lambda = GetExpressionLambdaMethod()
.MakeGenericMethod(delegateType)
.Invoke(null, new object[] { property, new[] { parameter } });
return lambda;
}
public static MethodInfo GetExpressionLambdaMethod()
{
return typeof(Expression)
.GetMethods()
.Where(m => m.Name == "Lambda")
.Select(m => new
{
Method = m,
Params = m.GetParameters(),
Args = m.GetGenericArguments()
})
.Where(x => x.Params.Length == 2
&& x.Args.Length == 1
)
.Select(x => x.Method)
.First();
}
}
Usage -
var expression = testObject.Build("YourPropertyName");
Now this will build the Expression you desired with return type of property. But since we don't know about your library but I suggest you to call your library method via reflection and pass the expression wrapped under object.
As I mentioned in the comments, building expression without knowing the property type is easy (even with nested property support):
static LambdaExpression MakeSelector(Type objectType, string path)
{
var item = Expression.Parameter(objectType, "item");
var body = path.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
return Expression.Lambda(body, item);
}
But then you'll need to find a way to call your generic library method - using reflection or dynamic call.
If you have both ObjectType and ObjectPropertyType as generic type parameters, you can use the Expression class to do something like this:
public static Expression<Func<TObject, TPropertyType>> Generate<TObject, TPropertyType>(
string property_name)
{
var parameter = Expression.Parameter(typeof (TObject));
return Expression.Lambda<Func<TObject, TPropertyType>>(
Expression.Property(parameter, property_name), parameter);
}
There is old intresting library DynamicLinq. May be it will be useful for you. It extends System.Linq.Dynamic to support execution of Lambda expressions defined in a string. With use of DynamicLinq you can do somethink like:
public class IndexViewModel
{
public bool HasPassword { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
//...........
Expression<Func<IndexViewModel, bool>> ex =
System.Linq.Dynamic.DynamicExpression.ParseLambda<IndexViewModel, bool>("TwoFactor");
var model = new ReactJs.NET.Models.IndexViewModel() { TwoFactor = true };
var res = ex.Compile()(model);
// res == true
System.Diagnostics.Debug.Assert(res);
I have some code which dynamically create a lambda starting from strings. For example, I have a filter class like the following:
public class Criteria {
public string Property { get; set; }
public string Operator { get; set; }
public string Value { get; set; }
}
And I am able to create a lambda like x => x.Name == "Foo" starting from a Criteria instance like this
Criteria c = new Criteria() {
Property = "Name",
Operator = "equal",
Value = "Foo"
}
Supposing to have a class like
public class Receipt {
public string Name { get; set; }
public int Amount { get; set; }
[other props omitted]
public ICollection<ReceiptDetail> Details { get; set; }
}
I would like to:
Apply the lambda to any object (I know that the lambda should be created with a ParameterExpression of the Receipt class)
Getting back the boolean result of the lambda (e.g. Is the name equals to Foo?)
Apply the same logic to collections Count() method (e.g. Building a lambda which checks against receipt.Details.Count()
Is this possible?
EDIT: As per the comments, I am elaborating my needs a bit more. This code will give me the chance to answer to a requirement that I have and that says: If there is a rule specified for my object then the application should behave a bit differently. While this is a common requirement, I would like to create a code which will permit me to extend it as more rules will be added. Actually I only have 5 rule types:
Verify if the input comes in a specific day of the week
Verify if the input comes in a specific time range
Verify if the field "X" of the input is less/equal/greather than a value
Verify if the field "Y" of the input contains a value
Verify if the field "Z" of the input, which is a collection, has a count that is less/equal/greather than a value
For the first 4 points I have been able to dynamically create a lambda expression, with code like in P.Brian.Mackey answer, which I could apply, using the Specification pattern, to the object itself.
The last point needs to be implemented almost in the same way but the only difference is that the left part of the expression was a method call and not a Property (specifically the ICollection<T>.Count() method)
Here's something to get you started. There is plenty of room for improvement. Especially the ugly factory. This demo is intended to show how to use Expressions to solve the problems, not as a best practices or factory pattern demo. If anything is unclear please feel free to ask for clarification.
Usage
[Test]
public void ApplySameLogicToCollectionsCount()
{
var receipt = new Receipt();
var details = new ReceiptDetail();
var details2 = new ReceiptDetail();
receipt.Details.Add(details);
receipt.Details.Add(details2);
var result = LambdaGeneratorFactory<ICollection<ReceiptDetail>>.Run(detailsCount);
Assert.IsTrue(result(receipt.Details));
}
Factory
public static class LambdaGeneratorFactory<T>
{
//This is an ugly implementation of a Factory pattern.
//You should improve this possibly with interfaces, maybe abstract factory. I'd start with an ICriteria.
public static Predicate<T> Run(Criteria criteria)
{
if (typeof(T) == typeof (Receipt))
{
return CreateLambda(criteria);
}
else if (typeof (T) == typeof (ICollection<ReceiptDetail>))
{
return CreateLambdaWithCount(criteria);
}
return null;
}
private static Predicate<T> CreateLambda(Criteria criteria)
{
ParameterExpression pe = Expression.Parameter(typeof(T), "i");
Expression left = Expression.Property(pe, typeof(T).GetProperty(criteria.Property));
Expression right = Expression.Constant(criteria.Value);
Expression predicateBody = Expression.Equal(left, right);
var predicate = Expression.Lambda<Predicate<T>>(predicateBody, new ParameterExpression[] { pe }).Compile();
return predicate;
}
private static Predicate<T> CreateLambdaWithCount(Criteria criteria)
{
ParameterExpression pe = Expression.Parameter(typeof(T), "i");
Expression count = Expression.Property(pe, typeof(T).GetProperty("Count"));
Expression left = Expression.Call(count, typeof(Object).GetMethod("ToString"));
Expression right = Expression.Constant(criteria.Value);
Expression predicateBody = Expression.Equal(left, right);
var predicate = Expression.Lambda<Predicate<T>>(predicateBody, new ParameterExpression[] { pe }).Compile();
return predicate;
}
}
Criteria
private Criteria detailsCount = new Criteria()
{
Property = "Details",
Operator = "equal",
Value = "2"
};
Switch to ICriteria and things will b cleaner. A better factory and no need for ToString. Program to an interface.
All that being said, this code feels a bit funky. What is the point of generating functions from strings? I get the feeling this is heading towards generating C# from a grammar. I'm not convinced that will scale well. For non-trivial implementations consider lex/yacc first. You can find more details for doing this in the Pragmatic Programmer "Implementing a mini language".
Yours is a fascinating question, and I'd like to understand the requirements. I created a demo, and I'm wondering, how does the demo differ from what you're trying to accomplish? There is a working version here https://dotnetfiddle.net/AEBZ1w too.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
Criteria c = new Criteria() {
Property = "Name",
Operator = "==",
Value = "Foo" };
var queryable = (new List<Receipt>() {
new Receipt { Name = "Foo", Amount = 1 },
new Receipt { Name = "Foo", Amount = 2 },
new Receipt { Name = "Bar" }
}).AsQueryable();
var parameter = Expression.Parameter(typeof(Receipt), "x");
var property = Expression.Property(parameter, typeof(Receipt).GetProperty(c.Property));
var constant = Expression.Constant(c.Value);
var operation = Expression.Equal(property, constant);
var expression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { queryable.ElementType },
queryable.Expression,
Expression.Lambda<Func<Receipt, bool>>(operation, new ParameterExpression[] { parameter })
);
Console.WriteLine("Linq Expression: {0} \n", expression.ToString());
Console.WriteLine("Results: \n");
var results = queryable.Provider.CreateQuery<Receipt>(expression);
foreach(var r in results)
{
Console.WriteLine("{0}:{1}", r.Name, r.Amount);
}
}
}
public class Criteria
{
public string Property, Operator, Value;
}
public class ReceiptDetail
{
public string ItemName;
}
public class Receipt
{
public string Name { get; set; }
public int Amount;
public ICollection<ReceiptDetail> Details;
}
References
How to: Execute Expression Trees
How to: Use Expression Trees to Build Dynamic Queries
IQueryable Interface
Expression Methods
Expression.Property Method
You could use reflection with generics. One way to solve the problem could be an extension
public static class EnumerableExtensions
{
public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Criteria c)
{
var sourceType = typeof(T);
var propertyMember = sourceType.GetProperty(c.Property);
Func<string, bool> predicate = null;
switch (c.Operator)
{
case "equal":
predicate = (v) => v == c.Value;
break;
// other operators
default:
throw new ArgumentException("Unsupported operator.");
}
return source.Where(v => predicate((string)propertyMember.GetMethod.Invoke(v, null)));
}
}
Which you could make use of in your code:
void FooBar()
{
Criteria c = new Criteria()
{
Property = "Name",
Operator = "equal",
Value = "foo"
};
var source = new Receipt[2];
source[0] = new Receipt { Name = "foo", Amount = 1 };
source[1] = new Receipt { Name = "bar", Amount = 2 };
var result = source.Where(c);
}
This is just to give you an idea. Improvements would be error handling (property not found, invalid cast, null values, etc.), refactoring to enable unit testing (e.g. inject the select "strategy") and performance (e.g. building, compiling and caching expression trees instead of reflection). This should give you enough keywords to learn about. Hope this helps.
I have two expression trees defined like this:
private Expression<Func<TEntity, TPropertyResult>> PropertyAccessor { get; set; }
and
private Expression<Func<TPropertyResult, bool>> TestExpression { get; set; }
I need to create a new expression tree that will result in the equivalent of:
var expression = p => this.TestExpression(this.PropertyAccessor(p));
When using Expression.Invoke(this.TestExpression, this.PropertyAccessor), I get the following error
{"Expression of type
'System.Func`2[MyEntity,System.String]'
cannot be used for parameter of type
'System.String'"}
TPropertyResult is a string during my test.
I tried using Expression.Call or Expression.Invoke. No luck. What should I use?
I think this does what you are asking for:
Expression<Func<TEntity, bool>> Combined
{
get
{
var entity = Expression.Parameter(typeof(TEntity));
var pa = Expression.Invoke(PropertyAccessor, entity);
var te = Expression.Invoke(TestExpression, pa);
return (Expression<Func<TEntity, bool>>) Expression.Lambda(te, entity);
}
}
I tested this and it works as I would expect.
However, re-reading your original question (before my edits), I am beginning to get the impression that you asked the wrong question and that you probably don’t need expression trees. If all you need is functions, then you can use them without Expression:
private Func<TEntity, TPropertyResult> PropertyAccessor { get; set; }
private Func<TPropertyResult, bool> TestExpression { get; set; }
private Func<TEntity, bool> Combined
{
get
{
return entity => TestExpression(PropertyAccessor(entity));
}
}
Example of use:
// Set up the original functions
PropertyAccessor = entity => GenerateResult(entity);
TestExpression = result => result.IsCool();
// This stores a reference to the combined function
var fn = Combined;
// This actually evaluates the function
bool isCool = fn(myEntity);
// Alternatively, you could evaluate the function directly, without the variable
bool isCool = Combined(myEntity);
The easiest way I found to do this is by using LinqKit (https://github.com/scottksmith95/LINQKit)
With it you can actually do
var expression = p => this.TestExpression.Invoke(this.PropertyAccessor(p));
db.Users.Where(expression.Expand());
Expand comes with LinqKit and does the magic here, it allows EF to be able to do the translation to SQL despite having the Invoke in your expression.