Pass expression parameter as argument to another expression - c#

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.

Related

How to capture an expression tree in C#

I am writing some debug/test code in which I want to both display the original expression and evaluate it, for any arbitrary expression.
For (trivial) example:
IList<string> myString = /* Some string collection expression */
ShowMe(myString.Select(s => s.ToLower()));
Where I implement ShowMe something like:
public void ShowMe(/* Not sure what has to go here */)
{
/* Not sure what has to go here */
Console.WriteLine(expression.ToString();
IEnumerable result = expression.Evaluate(); // or something
foreach(item in result)
{
Console.WriteLine(/* etc. */)
}
}
And the result will be written to the console as:
myString.Select(s => s.ToLower())
(first item)
(next item
(etc...)
In other words, my ShowMe method operates on the expression tree rather than the value of the expression, so that it can both display the expression it was given as well as the computed result.
I can't simply declare ShowMe as:
public void ShowMe(Expression expr)
... but if I declare is as
public void ShowMe(Expression<Func<Enumerable>> expr)
... it sort-of works - I have to call my method with a lambda expression thus:
ShowMe(() => myString.Select(s => s.ToLower()))
... which I don't want to do.
I'm reasonably sure this can be done... FluentAssertions does it. For example: if I execute the following line of test code:
(1 + 1).Should.Be(3)
I get the following result:
Expected (1 + 1) to be 3, but found 2.
The FluentAssertion has both evaluated the expression (1+1) and captured the expression tree so that was able to display the original expression it evaluated.
I can't see how this was done, but I want to do similar. How do I do it?
This is per se not possible with any method.
All those libraries just parse the stack trace and extract the file name as well as the line number. Then the expression is extracted from the source code file at the given line (whilst this includes some parsing/validation).
It is also notably, that the expression can't be shown, if the source code does not exists/is not available.
Figured out an acceptable compromise:
public static class ObjectHelper
{
public static void ToConsole<T>(this IEnumerable<T> enumerable, Expression<Func<T,object>> expr)
where T:class
{
var fn = expr.Compile();
var result = enumerable.Select(s => fn(s));
Console.WriteLine($"My data selected as {PrettyPrintExpression(expr)}");
foreach(var element in result)
{
Console.WriteLine(/* etc. */);
}
}
private static string PrettyPrintExpression(Expression<Func<T,object>> expr)
{
// Walk the expression tree to print as desired
}
}
...which I can invoke as:
IList<MyObject> list = /* etc. */
list.ToConsole(s => new{/* any members I want out of MyObject */});

Expression parsing - Possible to get array of property names as string?

Is it possible to complete this method? Is it possible in the latest version of C#? Thinking about this as a DSL to configure a system for watching for certain property changes on certain objects.
List<string> list = GetProps<AccountOwner>(x => new object[] {x.AccountOwnerName, x.AccountOwnerNumber});
// would return "AccountOwnerName" and "AccountOwnerNumber"
public List<string> GetProps<T>(Expression<Func<T, object[]>> exp)
{
// code here
}
In C# 6, you'd use:
List<string> list = new List<string>
{
nameof(AccountOwner.AccountOwnerName),
nameof(AccountOwner.AccountOwnerNumber)
};
Before that, you could certainly break the expression tree apart - the easiest way of working out how is probably to either use an expression tree visualizer, or use the code you've got and put a break point in the method (just make it return null for now) and examine the expression tree in the debugger. I'm sure it won't be very complicated - just a bit more than normal due to the array.
You could possibly simplify it using an anonymous type, if you use:
List<string> list = Properties<AccountOwner>.GetNames(x => new {x.AccountOwnerName, x.AccountOwnerNumber});
Then you could have:
public static class Properties<TSource>
{
public static List<string> GetNames<TResult>(Func<TSource, TResult> ignored)
{
// Use normal reflection to get the properties
}
}
If you don't care about the ordering, you could just use
return typeof(TResult).GetProperties().Select(p => p.Name).ToList();
If you do care about the ordering, you'd need to look at the names the C# compiler gives to the constructor parameters instead - it's a bit ugly. Note that we don't need an expression tree though - we only need the property names from the anonymous type. (An expression tree would work just as well, admittedly.)
Without c# 6 and nameof, you could get a property name from a expression tree like:
using System.Linq.Expressions;
//...
static string GetNameOf<T>(Expression<Func<T>> property)
{
return (property.Body as MemberExpression).Member.Name;
}
Using it like:
GetNameOf(() => myObject.Property);
Not directly usable for an array of objects, but you could make an overload to take an array of expressions... something like:
static string[] GetNameOf(IEnumerable<Expression<Func<object>>> properties)
{
return properties.Select(GetNameOf).ToArray();
}
And use it like
GetNameOf(
new Expression<Func<object>>[]
{
() => x.AccountOwnerName,
() => x.AccountOwnerNumber
}
);
Demonstrating fiddle: https://dotnetfiddle.net/GsV96t
Update
If you go this route, the original GetNameOf for a single property won't work for value types (since they get boxed to object in the Expression and now the expression uses Convert internally). This is easily solvable by changing the code to something like:
static string GetNameOf<T>(Expression<Func<T>> property)
{
var unary = property.Body as UnaryExpression;
if (unary != null)
return (unary.Operand as MemberExpression).Member.Name;
return (property.Body as MemberExpression).Member.Name;
}
Updated fiddle: https://dotnetfiddle.net/ToXRuu
Note: in this updated fiddle I've also updated the overloaded method to return a List instead of an array, since that's what was on your original code

Nhibernate escaping special character within linq query

I have a table like:
TABLE_XY
|ID |SOURCE|
|1 |value_aa|
|2 |other_aa|
|3 |eeeaa|
And the generated query should be:
select * from TABLE_XY where SOURCE like '%\_aa' ESCAPE '\'​
I know that there are two options which would fit my needs
var result =
session.QueryOver<TableXy>()
.WhereRestrictionOn(x => x.Source)
.IsLike("%\_aa", MatchMode.Exact, '\\')
.List();
or
var result2 = session
.CreateCriteria<TableXy>()
.Add(LikeExpression("Source", "%\\_aa", MatchMode.Exact, '\\', false))
.List();
But I have to use a Linq based implementation. I'm working with dynamically created expression trees, which sometimes will be executed with the Linq to Object Provider or Linq to Nhibernate.
But currently only this method is supported:
var result = session
.Query<TableXy>()
.Where(x => NHibernate.Linq.SqlMethods.Like(x.Source, "%\\_aa"))
.ToList();
How can I extend the Nhibernate Linq Provider to support?
SqlMethods.IsLike(string source, string pattern, char? escape);
Okay, this is a pretty involved answer and there may very well be problems with it, but I was able to get a like operator with an escape piece working.
This involves a few steps, but basically what we're doing is adding a new type of HqlTreeNode that can handle the escape portion of the like operator.
Create an extension method that you'll use in LINQ queries. This method does not need an implementation--we'll provide that later:
public static class LinqExtensions
{
public static bool IsLikeWithEscapeChar(
this string input,
string like,
char? escapeChar)
{
throw new NotImplementedException();
}
}
Create an HqlEscape tree node that we will use to represent the escape portion of the like operator:
public class HqlEscape : HqlExpression
{
public HqlEscape(IASTFactory factory, params HqlTreeNode[] children)
: base(HqlSqlWalker.ESCAPE, "escape", factory, children)
{
}
}
Create an HqlLikeWithEscape tree node. The default HqlLike node cannot handle the escape part, so we need to create a new node that can handle three children:
public class HqlLikeWithEscape : HqlBooleanExpression
{
public HqlLikeWithEscape(IASTFactory factory, HqlExpression lhs, HqlExpression rhs, HqlEscape escape)
: base(HqlSqlWalker.LIKE, "like", factory, lhs, rhs, escape)
{
}
}
Create a generator for the IsLikeWithEscapeChar extension method we defined earlier. This class' responsibility is to take the information the method is invoked with and return an HQL tree structure that will ultimately be turned into SQL:
public class CustomLikeGenerator : BaseHqlGeneratorForMethod
{
public CustomLikeGenerator()
{
this.SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition(
() => LinqExtensions.IsLikeWithEscapeChar(null, null, null))
};
}
public override HqlTreeNode BuildHql(
MethodInfo method,
System.Linq.Expressions.Expression targetObject,
ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
// Is there a better way to do this?
var factory = new ASTFactory(new ASTTreeAdaptor());
HqlTreeNode escapeCharNode = visitor.Visit(arguments[2]).AsExpression();
var escapeNode = new HqlEscape(factory, escapeCharNode);
HqlLikeWithEscape likeClauseNode =
new HqlLikeWithEscape(
factory,
visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(arguments[1]).AsExpression(),
escapeNode);
return likeClauseNode;
}
}
As you can see, we've utilized the new HQL tree nodes we defined earlier. The major downside to this approach is that it required me to manually create an ASTFactory and ASTTreeAdaptor. The use of these classes is usually encapsulated inside of HqlTreeBuilder, but HqlTreeBuilder doesn't lend itself to being subclassed. Would appreciate some input on this if someone has some advice.
Create a new LINQ to HQL generators registry. This class just just associates our extension method with the HQL implementation we provided in step 4:
public class LinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public LinqToHqlGeneratorsRegistry() : base()
{
RegisterGenerator(
ReflectionHelper.GetMethodDefinition(() => LinqExtensions.IsLikeWithEscapeChar(null, null, null)),
new CustomLikeGenerator());
}
}
Update your configuration to use the new LinqToHqlGeneratorsRegistry:
cfg.LinqToHqlGeneratorsRegistry<LinqToHqlGeneratorsRegistry>();
(Finally) use your new extension method in a query:
session.Query<Person>().Where(p => p.FirstName.IsLikeWithEscapeChar("%Foo", '\\'))
Note that you need to specify the wildcard character. This could be smoothed out, but that wouldn't be too hard to do.
This is the first time I've extended HQL this way so again, there could be issues with this solution. I was only able to test on SQL Server, but I'm reasonable confident it should work given that it creates the same tree structure that an HQL query does.
Solution here, should be surprisingly simple:
var result = session
.Query<TableXy>()
// instead of this
//.Where(x => NHibernate.Linq.SqlMethods.Like(x.Source, "%\\_aa"))
// This will add sign % at the beginning only
.Where(x => x.Source.EndsWith("[_]aa"));
// or wrap it on both sides with sign: %
.Where(x => x.Source.Contains("[_]aa"));
.ToList();
Trick is to use ruglar like expression style for underscore [_]

How to parse expression tree to extract a type condition OfType

I am trying to implement the Queryable interface and I would like to extract the type from the expression.
I have implemented the Queryable using the sample in http://blogs.msdn.com/b/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx.
When I reach the Execute method in my Provider, the expression is :
expression = {value(TestApp.HouseQueryable`1[TestApp.House]).OfType().Where(i => (i.Address == "N.125 Oxford St."))}
The first argument seem to be the type but from there on I am not sure how to extract it from the OfType() method. Can someone help me on this ?
The code to build the queryable and the query provider are the one from the tutorial.
Thanks
Edit: To expand more on my goal, I would like to query from different services depending on the type given. I know that it is not the best way to do it since i will end up having a big IF ELSE in my query provider.
By following the response of Ani, I inherited from ExpressionVisitor and checked for method call to extract the type. I only tested my scenario which is having only one .OfType() and it seems to work.
public class ExpressionTreeModifier : ExpressionVisitor
{
public ExpressionTreeModifier(Expression expression)
{
this.Visit(expression);
}
protected override Expression VisitMethodCall(MethodCallExpression methodCall)
{
var method = methodCall.Method;
if (method.Name == "OfType" && method.DeclaringType == typeof(Queryable))
{
var type = method.GetGenericArguments().Single();
OfType = (Type)type;
}
return base.VisitMethodCall(methodCall);
}
public Type OfType { get;set; }
}
I'm not sure where you're going with this exactly, but to answer what you ask (and nothing more):
IQueryable queryable = ...
var methodCall = queryable.Expression as MethodCallExpression;
if(methodCall != null)
{
var method = methodCall.Method;
if(method.Name == "OfType" && method.DeclaringType == typeof(Queryable))
{
var type = method.GetGenericArguments().Single();
Console.WriteLine("OfType<{0}>", type);
}
}
Not sure how this highly specific bit of code is going to help you write your own query-provider, honestly. Could you expand a bit on your broader goals?

Get parameters from lambda Expression

I am not very familiar with lambda expressions. So I have the following expression:
EnabledPropertySelector = l => l.FranchiseInfo.ExternalSystemType == ExternalSystemTypes.Master
And two properties:
public string VisibilityPropertyName { get; set; }
public object VisibilityPropertyValue { get; set; }
I want to extract some data from the expression so in the end I can get the values of the two properties:
VisibilityPropertyName == 'FranchiseInfo.ExternalSystemType';
VisibilityPropertyValue == ExternalSystemTypes.Master;
VisibilityPropertyName is always a string. This is the name of the property.
VisibilityPropertyValue can be of any type.
EDIT:
I have a lot of properties. Some of them are dependent on other properties. For every single property I have to manually write the name and the value of the parent property:
{ VisibilityPropertyName = 'FranchiseInfo.ExternalSystemType', VisibilityPropertyValue = ExternalSystemTypes.Master, EnabledPropertySelector = l => l.FranchiseInfo.ExternalSystemType == ExternalSystemTypes.Master}
Instead of writing all this I want to write only the expression and populate the properties from it.
This is the declaration of the expresion:
Expression<Func<TEntity, bool?>> EnabledPropertySelector
First off all, you need an Expression. What's the type of EnabledPropertySelector? It'll need to be something like Expression<Func<T, bool>> where T is whatever the type of "l" in your example is.
If you already have an Expression then you can use the Expression API to extract whatever you need:-
var body = EnabledPropertySelector.Body as BinaryExpression;
var left = body.Left as PropertyExpression;
var outerMemberName = left.Member.Name;
var innerMemberName = (left.Expression as PropertyExpression).Member.Name
VisibilityPropertyName = innerMemberName + "." + outerMemberName;
var right = body.Right as PropertyExpression;
var rightValueDelegate = Expression.Lambda<Func<object>>(right).Compile();
VisibilityPropertyValue = rightValueDelegate();
etc.
I really recommend doing some reading to properly grok the expression API before diving in though; there are a lot of corner cases depending on how flexible you need to be. E.g. is the expression always of the form parameter.Property.Property == constant? It gets really complicated really quickly, so you'll want a solid understanding of the fundamentals before trying to handle any real-world cases.
There's a reasonable introduction to expression trees on MSDN, but some focused googling might get you a better understanding quicker.
You can use Funciton and Action class, I'm not very sure of what you want be able to do, but I can give an tip.
Functions returns a value:
Function<InputType1,InputType2,ResultType> functionVariableName;
Usage:
functionVariableName = (param1, param2) => {
//...process both params
return result;
};
Actions, do not return values:
Action<InputType1,InputType2> actionVariableName;
Usage:
actionVariableName= (param1, param2) => {
//...process both params
};
If the lambda expression is simple (one line, with out if expression) you can make the lambda with out {}:
functionVariableName = (param1, param2) => //process values and result;
Hope this helps...
if you want to create an IEnumerable where the two properties are equal:
var results = EnabledPropertySelector.Where(l => l.FranchiseInfo.ExternalSystemType ==
ExternalSystemTypes.Master.ToString());

Categories

Resources