Use LinqKit PredicateBuilder for related model (EF Core) - c#

I want to use LinqKit's PredicateBuilder and pass the predicate into .Any method for related model.
So I want to build a predicate:
var castCondition = PredicateBuilder.New<CastInfo>(true);
if (movies != null && movies.Length > 0)
{
castCondition = castCondition.And(c => movies.Contains(c.MovieId));
}
if (roleType > 0)
{
castCondition = castCondition.And(c => c.RoleId == roleType);
}
And then use it to filter model that has relation to model in predicate:
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castCondition));
return await result.OrderBy(n => n.Name1).Take(25).ToListAsync();
But this causes a System.NotSupportedException: Could not parse expression 'n.CastInfo.Any(Convert(__castCondition_0, Func``2))': The given arguments did not match the expected arguments: Object of type 'System.Linq.Expressions.UnaryExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.
I saw similar question and answer there suggests to use .Compile. Or one more question that build an extra predicate.
So I tried to use extra predicate
var tp = PredicateBuilder.New<Name>(true);
tp = tp.And(n => n.CastInfo.Any(castCondition.Compile()));
IQueryable<Name> result = _context.Name.AsExpandable().Where(tp);
Or use compile directly
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castCondition.Compile()));
But I have an error about Compile: System.NotSupportedException: Could not parse expression 'n.CastInfo.Any(__Compile_0)'
So is it possible to convert the result from PredicateBuilder to pass into Any?
Note: I was able to build the desired behavior combining expressions, but I don't like that I need extra variables.
System.Linq.Expressions.Expression<Func<CastInfo,bool>> castExpression = (c => true);
if (movies != null && movies.Length > 0)
{
castExpression = (c => movies.Contains(c.MovieId));
}
if (roleType > 0)
{
var existingExpression = castExpression;
castExpression = c => existingExpression.Invoke(c) && c.RoleId == roleType;
}
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castExpression.Compile()));
return await result.OrderBy(n => n.Name1).Take(25).ToListAsync();
So I assume I just miss something about builder.
Update about versions: I use dotnet core 2.0 and LinqKit.Microsoft.EntityFrameworkCore 1.1.10

Looking at the code, one will assume that the type of castCondition variable is Expression<Func<CastInfo, bool>> (as it was in earlier versions of PredicateBuilder).
But if that was the case, then n.CastInfo.Any(castCondition) should not even compile (assuming CastInfo is a collection navigation property, so the compiler will hit Enumerable.Any which expects Func<CastInfo, bool>, not Expression<Func<CastInfo, bool>>). So what's going on here?
In my opinion, this is a good example of C# implicit operator abuse. The PredicateBuilder.New<T> method actually returns a class called ExpressionStarter<T>, which has many methods emulating Expression, but more importantly, has implicit conversion to Expression<Func<T, bool>> and Func<CastInfo, bool>. The later allows that class to be used for top level Enumerable / Queryable methods as replacement of the respective lambda func/expression. However, it also prevents the compile time error when used inside the expression tree as in your case - the complier emits something like n.CastInfo.Any((Func<CastInfo, bool>)castCondition) which of course causes exception at runtime.
The whole idea of LinqKit AsExpandable method is to allow "invoking" expressions via custom Invoke extension method, which then is "expanded" in the expression tree. So back at the beginning, if the variable type was Expression<Func<CastInfo, bool>>, the intended usage is:
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castCondition.Invoke(c)));
But now this doesn't compile because of the reason explained earlier. So you have to convert it first to Expression<Func<T, bool> outside of the query:
Expression<Func<CastInfo, bool>> castPredicate = castCondition;
and then use
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castPredicate.Invoke(c)));
or
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(castPredicate.Compile()));
To let compiler infer the expression type, I would create a custom extension method like this:
using System;
using System.Linq.Expressions;
namespace LinqKit
{
public static class Extensions
{
public static Expression<Func<T, bool>> ToExpression<T>(this ExpressionStarter<T> expr) => expr;
}
}
and then simply use
var castPredicate = castCondition.ToExpression();
It still has to be done outside of the query, i.e. the following does not work:
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castCondition.ToExpression().Invoke(c)));

It may not be exactly related to the original question, but considering the following model :
public Class Music
{
public int Id { get; set; }
public List<Genre> Genres { get; set; }
}
public Class Genre
{
public int Id { get; set; }
public string Title { get; set; }
}
List<string> genresToFind = new() {"Pop", "Rap", "Classical"};
If you are trying to find all Musics that their genres exist in genresToFind list, here's what you can do:
Create PredicateBuilder expressions chain on Genre model :
var pre = PredicateBuilder.New<Genre>();
foreach (var genre in genresToFind)
{
pre = pre.Or(g => g.Title.Contains(genre));
}
Then execute your query like this :
var result = await _db.Musics.AsExpandable()
.Where(m => m.Genres
.Any(g => pre.ToExpression().Invoke(g)))
.ToListAsync();
ToExpression() is a generic extension method that we've created to convert ExpressionStarter<Genre> type to Expression<Func<Genre, bool>> :
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> ToExpression<T> (this
ExpressionStarter<T> exp) => exp;
}
Also, you'll need LinqKit.Microsoft.EntityFrameworkCore package for efcore.

Related

Searching for records in several tables using EF TPT

I have a project with TPT inheritance mapping, now I need to add it a search functionality that will find records in several tables. That's what I currently have:
public abstract class Customer
{
public int Id { get; set; }
public string Memo { get; set; }
...
}
public class Person : Customer
{
public string GivenName { get; set; }
public string Surname { get; set; }
...
}
public class Company : Customer
{
public string Name { get; set; }
...
}
I also have a unit of work with bunch of repositories, and I need to add the filtering functionality to several methods of CustomerRepository. Let's say I have a Count method with the following signature
public int Count(System.Linq.Expressions.Expression<Func<Customer, bool>> filter = null)
Now I need to get the quantity of customers whose GiveName or Surname contains searchTerm in case the customer is a Person or the same searchTerm in Name field in case it is a Company.
TL;DR
How a view with a single, searchable, paged list of Customers (containing both Person and Company types) should be implemented? I mean in terms of method with signature like public IHttpActionResult Get(string searchTerm, int pageSize, int pageNumber)...
That's what I tried:
I added to each of the classes a static method that would generate an Expression to search that specific class, that's how it looks for the Person class:
public static System.Linq.Expressions.Expression<Func<Person, bool>> GetFilter(string searchTerm)
{
if (String.IsNullOrWhiteSpace(searchTerm))
{
return null;
}
var parameterExpression = System.Linq.Expressions.Expression.Parameter(typeof(Person));
System.Reflection.MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
return System.Linq.Expressions.Expression.Lambda<Func<Person, bool>>(
System.Linq.Expressions.Expression.OrElse(
System.Linq.Expressions.Expression.Call(
System.Linq.Expressions.Expression.PropertyOrField(parameterExpression, "GivenName"),
method,
System.Linq.Expressions.Expression.Constant(searchTerm, typeof(string))
),
System.Linq.Expressions.Expression.Call(
System.Linq.Expressions.Expression.PropertyOrField(parameterExpression, "Surname"),
method,
System.Linq.Expressions.Expression.Constant(searchTerm, typeof(string))
)
), parameterExpression);
}
And tried to build an Expression that would check the type of the customer and then make an appropriate data check, but here I stumped... That's what I have right now:
var parameterExpression = System.Linq.Expressions.Expression.Parameter(typeof(Customer));
var typeIsPerson = System.Linq.Expressions.Expression.TypeIs(parameterExpression, typeof(Person));
var typeIsCompany = System.Linq.Expressions.Expression.TypeIs(parameterExpression, typeof(Company));
var q = System.Linq.Expressions.Expression.Block(
System.Linq.Expressions.Expression.IfThen(typeIsPerson, Person.GetFilter(searchTerm)),
System.Linq.Expressions.Expression.IfThen(typeIsCompany, Company.GetFilter(searchTerm)),
System.Linq.Expressions.Expression.Constant(false));
var a = System.Linq.Expressions.Expression.Lambda<Func<Customer, bool>>(
q, parameterExpression);
Here I have two problems(at least?), first when I try to call Count, I get a very unpleasant NotSupportedException exception that says Unknown LINQ expression of type 'Block'. The second is that I don't know how to return the result of execution for each of GetFilters, I suspect that I will get false for any record since it is default value that is the last Expression in my Block...
May be I'm on a wrong track and this is something that should be done in a completely different manner?
Expression blocks are generally unsupported in LINQ to Entities. And normally you don't need them because you could build almost any expression just using C# conditional operator ? : (which maps to Expression.Condition).
But before even trying to build an expression dynamically, you need to find a EF supported construct working with TPT (and other EF inheritance models) polymorphic query. Which is not so easy because all the examples use OfType method which is only applicable when you need to filter a concrete derived entities. With some trial and error, luckily there are two supported constructs - is and as (important: as, not cast!).
So the statically built predicate expression in question could be like this:
Expression<Func<Customer, bool>> predicate = c =>
c is Person ?
((c as Person).GivenName.Contains(searchTerm) || (c as Person).Surname.Contains(searchTerm)) :
c is Company ?
(c as Company).Name.Contains(searchTerm) :
false;
(Frankly you don't want to look at the generated SQL, but it works)
Now you can build it dynamically if you wish. You already found the is expression method (Expression.TypeIs), for as operator the corresponding expression metod is Expression.TypeAs.
You do not need to do all that. Just create a generic method that you will close when you call it. Your generic method can be like this:
public static int Count<T>(Expression<Func<T, bool>> filter = null)
{
var ctx = new StackContext();
return ctx.Customers.OfType<T>().Where(filter).Count();
}
You can call that like this:
// Here you are closing the generic to be of type Person
var personsCount = Count<Person>(x => x.GivenName == "George");
// Here you are closing the generic to be of type Customer
var companyCount = Count<Company>(x => x.Name == "Test");

Is there a way to capture a lambda expression so that it's not compile-time forced to take on an identity as either an Expression or Delegate type?

Suppose I have a complex lambda expression as follows:
x => x.A.HasValue || (x.B.HasValue && x.C == q) || (!x.C.HasValue && !x.A.HasValue) || //...expression goes on
I want to use this as an Expression<Func<T,bool> in (e.g. Linq-To-Entities) Queryable.Where method. I also want to use it in the Enumerable.Where method, but the Where method only accepts a Func<T,bool>, not an Expression<Func<T,bool>.
The lambda syntax itself can be used to generate either an Expression<Func<T,bool>> or a Func<T,bool> (or any delegate type for that matter), but in this context it cannot generate more than one at once.
For example, I can write:
public Expression<Func<Pair,bool>> PairMatchesExpression()
{
return x => x.A == x.B;
}
as easily as I can write:
public Func<Pair,bool> PairMatchesDelegate()
{
return x => x.A == x.B;
}
The problem is that I cannot use the same exact lambda expression (i.e. x => x.A == x.B) in both ways, without physically duplicating it into two separate methods with two different return types, in spite of the compiler's ability to compile it into either one.
In other words, if I'd like to use the lambda expression in the Queryable methods, then I have to use the Expression method signature. Once I do that however, I cannot use it as a Func as easily as I could have had I just declared the method return type as Func. Instead, I now have to call Compile on the Expression and then worry about caching the results manually like so:
static Func<Pair,bool> _cachedFunc;
public Func<Pair,bool> PairMatchesFunc()
{
if (_cachedFunc == null)
_cachedFunc = PairMatchesExpression().Compile();
return _cachedFunc;
}
Is there a solution to this problem so that I can use the lambda expression in a more general way without it being locked down to a particular type at compile-time?
Unfortunately, I can see no way to truly get, at compile time, a Func and an Expression from the same lambda. However, you could at least encapsulate away the difference, and you can also defer the compilation of the Func until the first time it's used. Here's a solution that makes the best of things and may meet your needs, even though it doesn't quite go all the way to what you really wanted (compile-time evaluation of both the Expression and the Func).
Please note that this works fine without using the [DelegateConstraint] attribute (from Fody.ExtraConstraints), but with it, you will get compile-time checking of the constructor parameter. The attributes make the classes act like they have a constraint where T : Delegate, which is not currently supported in C#, even though it is supported in the ILE (not sure if I'm saying that right, but you get the idea).
public class VersatileLambda<[DelegateConstraint] T> where T : class {
private readonly Expression<T> _expression;
private readonly Lazy<T> _funcLazy;
public VersatileLambda(Expression<T> expression) {
if (expression == null) {
throw new ArgumentNullException(nameof(expression));
}
_expression = expression;
_funcLazy = new Lazy<T>(expression.Compile);
}
public static implicit operator Expression<T>(VersatileLambda<T> lambda) {
return lambda?._expression;
}
public static implicit operator T(VersatileLambda<T> lambda) {
return lambda?._funcLazy.Value;
}
public Expression<T> AsExpression() { return this; }
public T AsLambda() { return this; }
}
public class WhereConstraint<[DelegateConstraint] T> : VersatileLambda<Func<T, bool>> {
public WhereConstraint(Expression<Func<T, bool>> lambda)
: base(lambda) { }
}
The beauty of the implicit conversion is that in contexts where a specific Expression<Func<>> or Func<> is expected, you don't have to do anything at all, just, use it.
Now, given an object:
public partial class MyObject {
public int Value { get; set; }
}
That is represented in the database like so:
CREATE TABLE dbo.MyObjects (
Value int NOT NULL CONSTRAINT PK_MyObjects PRIMARY KEY CLUSTERED
);
Then it works like this:
var greaterThan5 = new WhereConstraint<MyObject>(o => o.Value > 5);
// Linq to Objects
List<MyObject> list = GetObjectsList();
var filteredList = list.Where(greaterThan5).ToList(); // no special handling
// Linq to Entities
IQueryable<MyObject> myObjects = new MyObjectsContext().MyObjects;
var filteredList2 = myObjects.Where(greaterThan5).ToList(); // no special handling
If implicit conversion isn't suitable, you can cast explicitly to the target type:
var expression = (Expression<Func<MyObject, bool>>) greaterThan5;
Note that you don't really need the WhereConstraint class, or you could get rid of VersatileLambda by moving its contents to WhereConstraint, but I liked making the two separate (as now you can use VersatileLambda for something that returns other than a bool). (And this difference is largely what sets apart my answer from Diego's.) Using VersatileLambda as it is now looks like this (you can see why I wrapped it):
var vl = new VersatileLambda<Func<MyObject, bool>>(o => o.Value > 5);
I have confirmed that this works perfectly for IEnumerable as well as IQueryable, properly projecting the lambda expression into the SQL, as proven by running SQL Profiler.
Also, you can do some really cool things with expressions that can't be done with lambdas. Check this out:
public static class ExpressionHelper {
public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
this Expression<Func<TFrom, TMiddle>> first,
Expression<Func<TMiddle, TTo>> second
) {
return Expression.Lambda<Func<TFrom, TTo>>(
new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
first.Parameters
);
}
// this method thanks to Marc Gravell
private class SwapVisitor : ExpressionVisitor {
private readonly Expression _from;
private readonly Expression _to;
public SwapVisitor(Expression from, Expression to) {
_from = from;
_to = to;
}
public override Expression Visit(Expression node) {
return node == _from ? _to : base.Visit(node);
}
}
}
var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);
You can create an overload of Chain that takes a VersatileLambda as the first parameter, and returns a VersatileLambda. Now you're really sizzling along.
You could create a wrapper class. Something like this:
public class FuncExtensionWrap<T>
{
private readonly Expression<Func<T, bool>> exp;
private readonly Func<T, bool> func;
public FuncExtensionWrap(Expression<Func<T, bool>> exp)
{
this.exp = exp;
this.func = exp.Compile();
}
public Expression<Func<T, bool>> AsExp()
{
return this;
}
public Func<T, bool> AsFunc()
{
return this;
}
public static implicit operator Expression<Func<T, bool>>(FuncExtensionWrap<T> w)
{
if (w == null)
return null;
return w.exp;
}
public static implicit operator Func<T, bool>(FuncExtensionWrap<T> w)
{
if (w == null)
return null;
return w.func;
}
}
And then it would be used like this:
static readonly FuncExtensionWrap<int> expWrap = new FuncExtensionWrap<int>(i => i == 2);
// As expression
Expression<Func<int, bool>> exp = expWrap;
Console.WriteLine(exp.Compile()(2));
// As expression (another way)
Console.WriteLine(expWrap.AsExp().Compile()(2));
// As function
Func<int, bool> func = expWrap;
Console.WriteLine(func(1));
// As function(another way)
Console.WriteLine(expWrap.AsFunc()(2));
Here is one workaround. It generates an explicit class for the expression (as the compiler would do under the hood anyway with lambda expressions that require a function closure) instead of just a method, and it compiles the expression in a static constructor so it doesn't have any race conditions that could result in multiple compilations. This workaround still incurs an additional runtime delay as a result of the Compile call which could otherwise be offloaded to build-time, but at least it's guaranteed to run only once using this pattern.
Given a type to be used in the expression:
public class SomeClass
{
public int A { get; set; }
public int? B { get; set; }
}
Build an inner class instead of a method, naming it whatever you would have named the method:
static class SomeClassMeetsConditionName
{
private static Expression<Func<SomeClass,bool>> _expression;
private static Func<SomeClass,bool> _delegate;
static SomeClassMeetsConditionName()
{
_expression = x => (x.A > 3 && !x.B.HasValue) || (x.B.HasValue && x.B.Value > 5);
_delegate = _expression.Compile();
}
public static Expression<Func<SomeClass, bool>> Expression { get { return _expression; } }
public static Func<SomeClass, bool> Delegate { get { return _delegate; } }
}
Then instead of using Where( SomeClassMeetsConditionName() ), you simply pass SomeClassMeetsConditionName followed by either .Delegate or .Expression, depending on the context:
public void Test()
{
IEnumerable<SomeClass> list = GetList();
IQueryable<SomeClass> repo = GetQuery();
var r0 = list.Where( SomeClassMeetsConditionName.Delegate );
var r1 = repo.Where( SomeClassMeetsConditionName.Expression );
}
As an inner class, it could be given an access level just like a method and accessed just like a method, and even collapsed all at once like a method, so if you can stand to look at the class instead of a method, this is a functional workaround. It could even be made into a code template.

How to develop a general method for linq statements?

I need to create a general routine in visual studio to get some parameters as input and return a list resulted from a repository. I am using Linq. But I am not sure how to develop this function and neither what key words I can use and find some resources.
This is a sample code that already is used in my program:
var lstReceiptDetails = Repository<TransactionDetail>()
.Where(current => current.HeaderId == headerId)
.OrderBy(current => current.DocumentRow)
.ToList();
I need to change the above linq statement to something like the following pseudocode:
private List<> GetQuery(repositoryName, conditionFieldName, orderFieldName )
{
var lstResult = Repository<repositiryName>()
.Where(current => current.ConditionFieldName == conditionFieldName)
.OrderBy(current => current.orderFieldName)
.ToList();
Return(lstResult);
}
Any help is appreciate.
Maryam
I think the closest way you can get is by using the following example below. I've tried a several ways to do this, but it would harm the usability and the readability. This is a compromise between code duplication and readability.
A sample POCO object:
class TransactionDetail
{
public DateTime DateProcessed { get; set; }
public string AccountName { get; set; }
}
The repositories:
abstract class GenericRepository<T>
{
public List<T> GetQuery<TKey>(
Func<T, bool> conditionFieldName,
Func<T, TKey> orderFieldName)
{
var lstResult = Repository()
.Where(conditionFieldName)
.OrderBy(orderFieldName)
.ToList();
return lstResult;
}
private IEnumerable<T> Repository()
{
throw new NotImplementedException();
}
}
class TransactionDetailRepository : GenericRepository<TransactionDetail>
{
}
And caller-side:
var repository = new TransactionDetailRepository();
var transactions = repository.GetQuery(
x => x.AccountName == "Foo Bar",
x => x.DateProcessed);
Argument checks should still be implemented properly though.
If this piece of code should be used in EntityFramework or Linq-to-SQL, parameters should be wrapped in Expression<T> such that, for example: Func<T, bool> becomes Expression<Func<T, bool>>
You can try to use the LINQ Dynamic Query Library that take string arguments instead of type-safe language operators.
Short example:
var result = Repository<repositoryName>().
Where("Id = 1").
Select("new(Id, Name)");
More information here: http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library

Passing an expression tree as a parameter to another expression tree

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.

Lambda Expression Tree Parsing

I am trying to use Lambda Expressions in a project to map to a third party query API. So, I'm parsing the Expression tree by hand.
If I pass in a lambda expression like:
p => p.Title == "title"
everything works.
However, if my lambda expression looks like:
p => p.Title == myaspdropdown.SelectedValue
Using the .NET debugger, I don't see the actual value of that funciton. Instead I see something like:
p => p.Title = (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue)
What gives? And when I try to grab the right side of the expression as a string, I get (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue) instead of the actual value. How do I get the actual value?
Remember that when you're dealing with the lambda expression as an expression tree, you don't have executable code. Rather you have a tree of expression elements, that make up the expression you wrote.
Charlie Calvert has a good post that discusses this in detail. Included is an example of using an expression visualiser for debugging expressions.
In your case, to get the value of the righthand side of the equality expression, you'll need to create a new lambda expression, compile it and then invoke it.
I've hacked together a quick example of this - hope it delivers what you need.
public class Class1
{
public string Selection { get; set; }
public void Sample()
{
Selection = "Example";
Example<Book, bool>(p => p.Title == Selection);
}
public void Example<T,TResult>(Expression<Func<T,TResult>> exp)
{
BinaryExpression equality = (BinaryExpression)exp.Body;
Debug.Assert(equality.NodeType == ExpressionType.Equal);
// Note that you need to know the type of the rhs of the equality
var accessorExpression = Expression.Lambda<Func<string>>(equality.Right);
Func<string> accessor = accessorExpression.Compile();
var value = accessor();
Debug.Assert(value == Selection);
}
}
public class Book
{
public string Title { get; set; }
}
To get the actual value, you need to apply the logic of the expression tree to whatever context you've got.
The whole point of expression trees is that they represent the logic as data rather than evaluating the expression. You'll need to work out what the lambda expression truly means. That may mean evaluating some parts of it against local data - you'll need to decide that for yourself. Expression trees are very powerful, but it's not a simple matter to parse and use them. (Ask anyone who's written a LINQ provider... Frans Bouma has bemoaned the difficulties several times.)
Just been struggling with exactly the same issue, thanks Bevan. On an extension, the following is a generic pattern you can use to extract the value (using this in my query engine).
[TestFixture]
public class TestClass
{
[Test]
public void TEst()
{
var user = new User {Id = 123};
var idToSearch = user.Id;
var query = Creator.CreateQuery<User>()
.Where(x => x.Id == idToSearch);
}
}
public class Query<T>
{
public Query<T> Where(Expression<Func<T, object>> filter)
{
var rightValue = GenericHelper.GetVariableValue(((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right.Type, ((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right);
Console.WriteLine(rightValue);
return this;
}
}
internal class GenericHelper
{
internal static object GetVariableValue(Type variableType, Expression expression)
{
var targetMethodInfo = typeof(InvokeGeneric).GetMethod("GetVariableValue");
var genericTargetCall = targetMethodInfo.MakeGenericMethod(variableType);
return genericTargetCall.Invoke(new InvokeGeneric(), new[] { expression });
}
}
internal class InvokeGeneric
{
public T GetVariableValue<T>(Expression expression) where T : class
{
var accessorExpression = Expression.Lambda<Func<T>>(expression);
var accessor = accessorExpression.Compile();
return accessor();
}
}
I'm not sure I understand. Where are you "seeing" that? Is that at design-time or run-time? Lambda expressions can be thought of essentially as anonymous delegates, and will operate with deferred execution. So you shouldn't expect to see the value assigned until after execution has passed that line, obviously.
I don't think that's really what you mean though... if you clarify the question a bit maybe I can help :)

Categories