Query with triple selector - c#

I'm currently have Lambda Expresion: http://pastebin.com/ZGCiQdHe
And i get ArgumentException in 53 line
http://msdn.microsoft.com/en-us/library/bb340145(v=vs.90).aspx
var lExpresion = Expression.Lambda<Func<TEntity, bool>>(body, parametrsNumber, parametrsTyp, parametrsLp);
But i not understand what i do wrong.
I want to get:
WHERE ((twr_gidnumer =1 and twr_gidtyp = 1 and twr_gidlp = 1) OR
(twr_gidnumer =1 and twr_gidtyp = 1 and twr_gidlp = 2))
--ANSWERR FULL
Based on article:
http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx
https://stackoverflow.com/Converting 2 argument Lambda expression to 1 argument Lambda expression
public static Expression<Func<CDNXL_TwrKarty, bool>> Bind2nd(Expression<Func<CDNXL_TwrKarty, CDNXL_TwrKarty,CDNXL_TwrKarty, bool>> source)
{
Expression newBody = new Rewriter(source.Parameters[0]).Visit(source.Body);
return Expression.Lambda<Func<CDNXL_TwrKarty, bool>>(newBody, source.Parameters[0]);
}
internal class Rewriter : ExpressionVisitor
{
private readonly ParameterExpression candidate_;
public Rewriter(ParameterExpression candidate)
{
candidate_ = candidate;
}
protected override Expression VisitParameter(ParameterExpression p)
{
return candidate_;
}
}
//REPLACE EXECUTING PLACE
var retMultiLamnda = Expression.Lambda<Func<CDNXL_TwrKarty, CDNXL_TwrKarty, CDNXL_TwrKarty, bool>>(body, parametrsNumber, parametrsTyp, parametrsLp);
var retOneLambda = Bind2nd(retMultiLamnda);
var retQuery = query.Where(retOneLambda);
return retQuery;
Thanx Rafal for Help.

First thing that I see is that your delegate type does not math with your invocation list. You expect to create function that receives one argument of type TEntity and yet you pass tree parameter expressions to function. Note that you execute this overload of Lablda method.
OK, I'll try to be more clear:
Expression.Lambda<Func<Arg1Type,...,ArgNType,ReturnType>>
(body,parameterExpressionForArg1,...,parameterExpressionForArgN);
Those generic arguments ArgType must match with parameterExpressionsForArg. There must be equal number of generic types for argumetnts as parameterExpressions for arguments. And types of those must also match.
So if you want to have tree parameterExpressions for some reason then you have to have tree arguments in your method:
Expression.Lambda<Func<CDNXL_TwrKarty,CDNXL_TwrKarty,CDNXL_TwrKarty, bool>>(body, parametrsNumber, parametrsTyp, parametrsLp);
might be correct call in this case obviously it wont match the Where call.
If you want to merge tree expressions into one matching Where argument then you have to replace all redundant ParamteterExpressions.

Related

How do I use reflection to get a property and use it in a query?

I have a generic method and I want to add a search capability to my method. as parameter I get the name of property(string) and the value(string) it should search for in the list. how can I achieve this?
**This code is not the exact code I have so it may seem that I can use other options like Expression functions which is not possible in my case cause it should be consumed in an Api Controller
**I use unit of work with repository pattern in real project and for sake of simplicity I have tryed to add it up in one simple function
public async Task<ActionResult<List<T>>> GetAll(string? filterProperty = null, string? filterValue = null)
{
IQueryable<T> query = dbSet;
if (filterProperty != null)
{
PropertyInfo property = typeof(T).GetProperty(filterProperty);
query = query. Where(u=> u.property.Contains(filterValue));
}
return await query.ToListAsync();
}
For IQueryable you'll want to create a LambdaExpression for the filter predicate. (For IEnumerable you can compile that expression into an appropriate Func<>.)
This all works by building an expression tree that represents the action you want to perform. In this case you're calling Contains on the result of getting the property value, passing a constant for the filter value.
Let's start with the Contains method, which you can reuse. Rather than basic reflection, here's how you can get it using an expression:
static readonly MethodInfo _contains =
(((Expression<Func<string, bool>>)(s => s.Contains("a"))).Body as MethodCallExpression)
.Method;
While that might look a little confusing, it's leveraging the compiler to do the reflection work for us. Sometimes it's easier than searching for the right version of a method with multiple overloads, or when it's not obvious which extension method is involved. The result here is that _contains gets initialized with the method info we need.
You've already got the property info for the target property, so let's put them together:
// The parameter for the predicate
var row = Expression.Parameter(typeof(T), "row");
// Constant for the filter value
var filter = Expression.Constant(filterValue);
// Get the value of the property
var prop = Expression.Property(property);
// Call 'Contains' on the property value
var body = Expression.Call(prop, _contains, filter);
// Finally, generate the lambda
var predicate = Expression.Lambda<Func<T, bool>(body, row);
// Apply to the query
query = query.Where(predicate);
Or in slightly more compact form:
var row = Expression.Parameter(typeof(T), "row");
var predicate =
Expression.Lambda<Func<T, bool>
(
Expression.Call
(
Expression.Property(row, property),
_contains,
Expression.Constant(filterValue)
),
row
);
When you're working on data via IEnumerable<T> instead, predicate.Compile() will produce a working Func<T, bool> to pass to IEnumerable.Where():
private static readonly MethodInfo _tostring = typeof(Object).GetMethod("ToString");
static readonly MethodInfo _compare = (((Expression<Func<string, bool>>)(s => s.Contains(""))).Body as MethodCallExpression).Method;
public static IEnumerable<T> Search<T>(this IEnumerable<T> items, string propertyName, string filterValue)
{
var property = typeof(T).GetProperty(propertyName);
var row = Expression.Parameter(typeof(T), "row");
// Let's make sure we're actually dealing with a string here
Expression prop = Expression.Property(row, property);
if (property.PropertyType != typeof(string))
prop = Expression.Call(prop, _tostring);
var func =
Expression.Lambda<Func<T, bool>>
(
Expression.Call
(
prop,
_compare,
Expression.Constant(filterValue)
),
row
).Dump().Compile();
return items.Where(func);
}
Expressions are pretty versatile, and there are a lot of places where they come in handy. It can be more efficient to compose a function and call it multiple times than to go through reflection all the time, and you can do interesting things with merging expressions and so on.

Can I dynamically create an expression representing a lambda which calls a method on the input parameter?

Let's say I have an object of a certain class A.
Let's say I need an Expression which calls method M on class A.
Is this even doable?
Basically, I need to programatically get this lambda
a => a.M();
The trick is, I want to do this generically, i.e. I plan to use reflection to figure out that the method is called M and what parameters it wants.
I tried using Expression.Lambda(MethodCallExpression method, ParameterExpression params).
The issue there is that when I define the method call expression, I have to specify an instance (or leave it at null if it's a static method, which it isn't). I don't want this. I want whatever the parameter is passed into the lambda (a in my example) to be the instance.
Is this possible?
Yes, it's possible to construct a linq expression at a runtime.
E.g. below is an example of constructing an expression of a method call which returns an object. This is really a dummy example as it's better to avoid object in favor of strict types.
static Expression<Func<T, object>> ComposeMethodCallExpressionAsFuncObject<T>(string methodName)
{
MethodInfo mi = typeof(T).GetMethod(methodName, types: new Type[0])
?? throw new ArgumentException($"There is no '{methodName}' method in the '{typeof(T).Name}' with the empty arguments list!");
var paramExpression = Expression.Parameter(typeof(T));
var methodCallExpression = Expression.Call(paramExpression, mi);
var result = Expression.Lambda<Func<T, object>>(methodCallExpression, paramExpression);
return result; // (T obj) =>obj.methodName()
}
, and example of usage:
int foo = 9988;
var expression = ComposeMethodCallExpressionAsFuncObject<int>(nameof(int.ToString));
//expression: (int obj) => obj.ToString()
var result = expression.Compile()(foo);
Assert.AreEqual("9988", result);

How to concatenate a property expression and a lambda using Select?

I would like to create the following expression dynamically:
e.Collection.Select(inner => inner.Property)
I created this code to do it, however I have an issue when I execute the expression call, someone knows what I'm doing wrong?
private static Expression InnerSelect<TInnerModel>(IQueryable source, ParameterExpression externalParameter, string complexProperty)
{
// Creates the expression to the external property. // this generates: "e.Collection".
var externalPropertyExpression = Expression.Property(externalParameter, complexProperty);
// Creates the expression to the internal property. // this generates: "inner => inner.Property"
var innerParameter = Expression.Parameter(typeof(TInnerModel), "inner");
var innerPropertyExpression = Expression.Property(innerParameter, "Property");
var innerLambda = Expression.Lambda(innerPropertyExpression, innerParameter);
return Expression.Call(typeof(Queryable), "Select", new [] { typeof(TInnerModel) }, externalPropertyExpression, innerLambda);
}
Error:
No generic method 'Select' on type 'System.Linq.Queryable' is
compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic.
So, first off, the primary problem is very simple. As the error message says, you haven't passed enough type arguments to Select. But when you fix that, you'll still have a problem, and that problem will be much harder for you to see and understand.
Let's dig into that.
You wish to represent this as an expression tree:
e.Collection.Select(inner => inner.Property)
Let's begin by rewriting it in its non-extension-method form.
Queryable.Select<A, B>(e.Collection, inner => inner.Property)
Where A is the collection member type and B is the type of Property.
Now, suppose you had this expression in your program. What would it actually do at runtime? It would construct an expression tree for the lambda and pass it to Queryable.Select. That is, it would do something like:
var innerParameter = parameterFactory(whatever);
var lambdaBody = bodyFactory(whatever);
var lambda = makeALambda(lambdaBody, innerParameter);
Queryable.Select<TInnerModel>(e.Collection, lambda);
Right? You with me so far?
Now, suppose we wish to translate this program fragment to an expression tree that could itself be the body of a lambda. That would be:
var theMethodInfoForSelect = whatever;
var receiverE = valueFactory(whatever);
var thePropertyInfoForCollection = whatever;
var theFirstArgument = propertyFactory(receiverE, thePropertyInfoForCollection);
...
Again, with me so far? Now, the crucial question: what is the second argument? It is NOT the value of lambda, which is what you are passing. Remember, what we are doing here is constructing an expression tree which represents the code that the compiler is generating for that thing, and the expression tree that is in lambda is not that. You're mixing levels!
Let me put it this way: the compiler is expecting "add one and three". You are passing 4. Those are very different things! One of them is a description of how to obtain a number and the other one is a number. You are passing an expression tree. What the compiler is expecting is a description of how to obtain an expression tree.
So: do you have to now write code that generates expression trees for all of lambda's construction code? Thank goodness no. We provided you a handy way to turn an expression tree into a description of how to produce an expression tree, which is the Quote operation. You need to use it.
So, what is the right sequence of events that you need to do to build your expression tree? Let's walk through it:
First, you'll need a ParameterExpression of the type of e, which you already have in hand. Let's suppose that is:
ParameterExpression eParam = Expression.Parameter(typeof(E), "e");
Next, you will need a method info for the Select method. Let's suppose you can correctly get that.
MethodInfo selectMethod = whatever;
That method takes two arguments, so let's make an array of argument expressions:
Expression[] arguments = new Expression[2];
You'll need a property info for your Collection property. I assume you can get that:
MethodInfo collectionGetter = whatever;
Now we can build the property expression:
arguments[0] = Expression.Property(eParam, collectionGetter);
Super. Next we need to start building that lambda. We need a parameter info for inner:
ParameterExpression innerParam = Expression.Parameter(typeof(Whatever), "inner");
We'll need a property info for Property, which I assume you can get:
MethodInfo propertyGetter = whatever;
Now we can build the body of the lambda:
MemberExpression body = Expression.Property(innerParam, propertyGetter);
The lambda takes an array of parameters:
ParameterExpression[] innerParams = { innerParam };
Build the lambda from the body and the parameters:
var lambda = Expression.Lambda<Func<X, int>>(body, innerParams);
Now the step you missed. The second argument is the quoted lambda, not the lambda:
arguments[1] = Expression.Quote(lambda);
Now we can build the call to Select:
MethodCallExpression callSelect = Expression.Call(null, selectMethod, arguments);
And we're done.
Give someone an expression tree and you give them an expression tree for a day; teach them how to find expression trees themselves and they can do it for a lifetime. How did I do that so fast?
Since I wrote the expression tree code generator, I had some immediate familiarity with the problem that you were likely to have. But that was ten years ago, and I did not do the above entirely from memory. What I did was I wrote this program:
using System;
using System.Linq.Expressions;
public interface IQ<T> {}
public class E
{
public IQ<X> C { get; set; }
}
public class X
{
public int P { get; set; }
}
public class Program
{
public static IQ<R> S<T, R>(IQ<T> q, Expression<Func<T, R>> f) { return null; }
public static void Main()
{
Expression<Func<E, IQ<int>>> f = e => S<X, int>(e.C, c => c.P);
}
}
Now I wished to know what code was generated by the compiler for the body of the outer lambda, so I went to https://sharplab.io/, pasted in the code, and then clicked on Results --> Decompile C#, which will compile the code to IL and then decompile it back to human-readable C#.
This is the best way I know of to quickly understand what the C# compiler is doing when it builds an expression tree, regardless of whether you know the compiler source code backwards and forwards. It's a very handy tool.

Pass expression parameter as argument to another expression

I have a query which filters results:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
return _context.Context.Quotes.Select(q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.Where(qpi => q.User.Id == qpi.ItemOrder))
});
}
In the where clause I'm using the parameter q to match a property against a property from the parameter qpi.
Because the filter will be used in several places I'm trying to rewrite the where clause to an expression tree which would look like something like this:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
return _context.Context.Quotes.Select(q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.AsQueryable().Where(ExpressionHelper.FilterQuoteProductImagesByQuote(q)))
});
}
In this query the parameter q is used as a parameter to the function:
public static Expression<Func<QuoteProductImage, bool>> FilterQuoteProductImagesByQuote(Quote quote)
{
// Match the QuoteProductImage's ItemOrder to the Quote's Id
}
How would I implement this function? Or should I use a different approach alltogether?
If I understand correctly, you want to reuse an expression tree inside another one, and still allow the compiler to do all the magic of building the expression tree for you.
This is actually possible, and I have done it in many occasions.
The trick is to wrap your reusable part in a method call, and then before applying the query, unwrap it.
First I would change the method that gets the reusable part to be a static method returning your expression (as mr100 suggested):
public static Expression<Func<Quote,QuoteProductImage, bool>> FilterQuoteProductImagesByQuote()
{
return (q,qpi) => q.User.Id == qpi.ItemOrder;
}
Wrapping would be done with:
public static TFunc AsQuote<TFunc>(this Expression<TFunc> exp)
{
throw new InvalidOperationException("This method is not intended to be invoked, just as a marker in Expression trees!");
}
Then unwrapping would happen in:
public static Expression<TFunc> ResolveQuotes<TFunc>(this Expression<TFunc> exp)
{
var visitor = new ResolveQuoteVisitor();
return (Expression<TFunc>)visitor.Visit(exp);
}
Obviously the most interesting part happens in the visitor.
What you need to do, is find nodes that are method calls to your AsQuote method, and then replace the whole node with the body of your lambdaexpression. The lambda will be the first parameter of the method.
Your resolveQuote visitor would look like:
private class ResolveQuoteVisitor : ExpressionVisitor
{
public ResolveQuoteVisitor()
{
m_asQuoteMethod = typeof(Extensions).GetMethod("AsQuote").GetGenericMethodDefinition();
}
MethodInfo m_asQuoteMethod;
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (IsAsquoteMethodCall(node))
{
// we cant handle here parameters, so just ignore them for now
return Visit(ExtractQuotedExpression(node).Body);
}
return base.VisitMethodCall(node);
}
private bool IsAsquoteMethodCall(MethodCallExpression node)
{
return node.Method.IsGenericMethod && node.Method.GetGenericMethodDefinition() == m_asQuoteMethod;
}
private LambdaExpression ExtractQuotedExpression(MethodCallExpression node)
{
var quoteExpr = node.Arguments[0];
// you know this is a method call to a static method without parameters
// you can do the easiest: compile it, and then call:
// alternatively you could call the method with reflection
// or even cache the value to the method in a static dictionary, and take the expression from there (the fastest)
// the choice is up to you. as an example, i show you here the most generic solution (the first)
return (LambdaExpression)Expression.Lambda(quoteExpr).Compile().DynamicInvoke();
}
}
Now we are already half way through. The above is enough, if you dont have any parameters on your lambda. In your case you do, so you want to actually replace the parameters of your lambda to the ones from the original expression. For this, I use the invoke expression, where I get the parameters I want to have in the lambda.
First lets create a visitor, that will replace all parameters with the expressions that you specify.
private class MultiParamReplaceVisitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> m_replacements;
private readonly LambdaExpression m_expressionToVisit;
public MultiParamReplaceVisitor(Expression[] parameterValues, LambdaExpression expressionToVisit)
{
// do null check
if (parameterValues.Length != expressionToVisit.Parameters.Count)
throw new ArgumentException(string.Format("The paraneter values count ({0}) does not match the expression parameter count ({1})", parameterValues.Length, expressionToVisit.Parameters.Count));
m_replacements = expressionToVisit.Parameters
.Select((p, idx) => new { Idx = idx, Parameter = p })
.ToDictionary(x => x.Parameter, x => parameterValues[x.Idx]);
m_expressionToVisit = expressionToVisit;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression replacement;
if (m_replacements.TryGetValue(node, out replacement))
return Visit(replacement);
return base.VisitParameter(node);
}
public Expression Replace()
{
return Visit(m_expressionToVisit.Body);
}
}
Now we can advance back to our ResolveQuoteVisitor, and hanlde invocations correctly:
protected override Expression VisitInvocation(InvocationExpression node)
{
if (node.Expression.NodeType == ExpressionType.Call && IsAsquoteMethodCall((MethodCallExpression)node.Expression))
{
var targetLambda = ExtractQuotedExpression((MethodCallExpression)node.Expression);
var replaceParamsVisitor = new MultiParamReplaceVisitor(node.Arguments.ToArray(), targetLambda);
return Visit(replaceParamsVisitor.Replace());
}
return base.VisitInvocation(node);
}
This should do all the trick.
You would use it as:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
Expression<Func<Quote, FilteredViewModel>> selector = q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.Where(qpi => ExpressionHelper.FilterQuoteProductImagesByQuote().AsQuote()(q, qpi)))
};
selector = selector.ResolveQuotes();
return _context.Context.Quotes.Select(selector);
}
Of course I think you can make here much more reusability, with defining expressions even on a higher levels.
You could even go one step further, and define a ResolveQuotes on the IQueryable, and just visit the IQueryable.Expression and creating a new IQUeryable using the original provider and the result expression, e.g:
public static IQueryable<T> ResolveQuotes<T>(this IQueryable<T> query)
{
var visitor = new ResolveQuoteVisitor();
return query.Provider.CreateQuery<T>(visitor.Visit(query.Expression));
}
This way you can inline the expression tree creation. You could even go as far, as override the default query provider for ef, and resolve quotes for every executed query, but that might go too far :P
You can also see how this would translate to actually any similar reusable expression trees.
I hope this helps :)
Disclaimer: Remember never copy paste code from anywhere to production without understanding what it does. I didn't include much error handling here, to keep the code to minimum. I also didn't check the parts that use your classes if they would compile. I also don't take any responsability for the correctness of this code, but i think the explanation should be enough, to understand what is happening, and fix it if there are any issues with it.
Also remember, that this only works for cases, when you have a method call that produces the expression. I will soon write a blog post based on this answer, that allows you to use more flexibility there too :P
Implementing this your way will cause an exception thrown by ef linq-to-sql parser. Within your linq query you invokes FilterQuoteProductImagesByQuote function - this is interpreted as Invoke expression and it simply cannot be parsed to sql. Why? Generally because from SQL there is no possibility to invoke MSIL method. The only way to pass expression to query is to store it as Expression> object outside of the query and then pass it to Where method. You can't do this as outside of the query you will not have there Quote object. This implies that generally you cannot achieve what you wanted. What you possibly can achieve is to hold somewhere whole expression from Select like this:
Expression<Func<Quote,FilteredViewModel>> selectExp =
q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.AsQueryable().Where(qpi => q.User.Id == qpi.ItemOrder)))
};
And then you may pass it to select as argument:
_context.Context.Quotes.Select(selectExp);
thus making it reusable. If you would like to have reusable query:
qpi => q.User.Id == qpi.ItemOrder
Then first you would have to create different method for holding it:
public static Expression<Func<Quote,QuoteProductImage, bool>> FilterQuoteProductImagesByQuote()
{
return (q,qpi) => q.User.Id == qpi.ItemOrder;
}
Application of it to your main query would be possible, however quite difficult and hard to read as it will require defining that query with use of Expression class.

How to create a generic lambda from two expression trees?

First of all I want to make you know that I've searched another questions and answers before making this one, but I cound't find any that would help me in this specific problem I am facing.
I need to filter registers based in two properties of its class, one of them being the field corresponding to the search, and the other one being a numeric code of another entity to which the register must be referenced in the database.
My search function has the following signature:
public List<TView> SearchByField(int parentCode, string fieldName, string filter);
I've tried to implement this using Expression Trees, and got it to get two expressions, but now I didn't get to combine these expressions to build one to pass to the final
Expression.AndAlso(parentCodeFilterExpression, textFilterExpression);
that will combine the too expressions in only one.
What I got so far was the code shown below (sorry for the long snippet, but I think this was necessary to make it easier to understand the question):
public List<TView> SearchPerField(int parentCode, string fieldName, string filter)
{
var lambdaExpression = GetLambdaExpressionForSearchByField(fieldName, filter, parentCode);
return new PersistenciaImpl<TView>().Where(lambdaExpression).ToList();
}
private Expression<Func<TView, bool>> GetLambdaExpressionForSearchByField(string fieldName, string filter, int parentCode)
{
Expression<Func<TView, bool>> textFilterExpression = GetTextFilterExpression(fieldName, filter);
Expression<Func<TView, bool>> parentCodeFilterExpression = GetParentCodeFilterExpression(parentCode);
Expression.Lambda<Func<TView, bool>>(textFilterExpression, parentCodeFilterExpression);
// THIS IS THE POINT. HOW TO MAKE THIS WORK?
Expression.AndAlso(parentCodeFilterExpression, textFilterExpression);
return textFilterExpression;
}
private Expression<Func<TView, bool>> GetParentCodeFilterExpression(int parentCode)
{
ParameterExpression parameter = Expression.Parameter(typeof(TView), "x");
Expression parent = Expression.Property(parameter, "Parent");
Expression parentCodeExpression = Expression.Property(parent, "Code");
Expression target = Expression.Constant(parentCode);
Expression containsMethod = Expression.Call(parentCodeExpression, "Equals", null, target);
Expression<Func<TView, bool>> textFilterExpression =
Expression.Lambda<Func<TView, bool>>(containsMethod, parameter);
return textFilterExpression;
}
private Expression<Func<TView, bool>> GetTextFilterExpression(string fieldName, string filter)
{
ParameterExpression parameter = Expression.Parameter(typeof(TView), "x");
Expression property = Expression.Property(parameter, fieldName);
Expression target = Expression.Constant(filter.ToUpper());
Expression containsMethod = Expression.Call(property, "Contains", null, target);
Expression<Func<TView, bool>> textFilterExpression =
Expression.Lambda<Func<TView, bool>>(containsMethod, parameter);
return textFilterExpression;
}
Thanks for any suggestion.
I've tried to implement this using Expression Trees, and got it to get two expressions, but now I didn't get to combine these expressions to build one to pass to the final
First, you need to declare a parameter for your final (outer) lambda. Then you need to invoke your two filter (inner) lambdas independently, passing in the same argument to each:
// using E = System.Linq.Expressions.Expression;
var item = E.Parameter(typeof(TView));
var combined = E.Lambda<Func<TView, bool>>(
E.AndAlso(
E.Invoke(parentCodeFilterExpression, item),
E.Invoke(textFilterExpression, item)),
item);
If you need these expressions to be compatible with a query provider like Entity Framework, things get a bit messier because Invoke expressions probably aren't supported. You'll have to manually inline the two filter lambdas, which requires walking each filter's body and replacing the inner parameter references with references to the outer lambda's parameter:
// using E = System.Linq.Expressions.Expression;
sealed class ParameterReplacementVisitor : ExpressionVisitor
{
private readonly IDictionary<E, E> _replacements;
public ParameterReplacementVisitor(IDictionary<E, E> replacements)
{
_replacements = replacements;
}
protected override Expression VisitParameter(ParameterExpression node)
{
E replacement;
if (_replacements.TryGetValue(node, out replacement))
return this.Visit(replacement);
return base.VisitParameter(node);
}
}
// ...
var item = E.Parameter(typeof(TView));
var visitor = new ParameterReplacementVisitor(
new Dictionary<E, E> {
{ parentCodeFilterExpression.Parameters[0], item },
{ textFilterExpression.Parameters[0], item }
}
);
var combined = E.Lambda<Func<TView, bool>>(
E.AndAlso(
visitor.Visit(parentCodeFilterExpression.Body),
visitor.Visit(textFilterExpression.Body)),
item);
Alternatively, if you are composing the inner expressions in a closed environment as your post suggests, you could simply pass the outer lambda parameter as an argument to the methods that construct the inner expressions, and return just the bodies (don't bother wrapping the inner filters in lambdas).
I think you need something like this:
MethodCallExpression where = Expression.Call((
typeof(Queryable),
"Where",
new Type[] { TView },
lambdaExpression );
Please note that I don't consider this a solution; it's merely an idea or example. Maybe this link will help you out.
You can compile an Expression<TDelegate> into a TDelegate by using the Compile method on the Expression:
Expression<Func<TView, bool>> lambdaExpression =
GetLambdaExpressionForSearchByField(fieldName, filter, parentCode);
Func<TView, bool> func = lambdaExpression.Compile();
Once you have that you can use it as a parameter for the Where function.
With the code above you can then use
return new PersistenciaImpl<TView>().Where(func).ToList();

Categories

Resources