Generic function for building Dynamic Linq query of string.contains against tokenized string - linq

I am using Expression.And and Expression.Or to build dynamic linq queries. When the property/field being queried is a string, and the string contains spaces I would like to tokenize the string on the spaces and create an "And'd" sub query on the tokens.
Here is what I mean in a non generic fashion
var tokens = Code.Split(new []{" "}, StringSplitOptions.RemoveEmptyEntries);
var index = 0;
var firstToken = tokens[index ++];
Expression<Func<Entity, bool>> subQuery =
entity => entity.Code.Contains(firstToken);
for (; index < tokens.Length; index ++)
{
var tempToken = tokens[index];
subQuery = subQuery.And(entity => entity.Code.Contains(tempToken));
}
query = query.Or(subQuery);
What I'd like to do is find a way of writing a method which is generic enough to just call for example:
PredicateBuilder.BuildTokenizedStringQuery<Entity>(
tokens, entity => entity.Code);
and I end up with the same result. The following is where I'm at but I can't use the Func stringProp accessor in and Expression. I have to somehow combine an accessor expression (of the string property) with an invocation expression (that invokes string.Contains)
private Expression<Func<T, bool>> BuildTokenizedStringQuery<T>(string[] tokens,
Func<T, string> stringProp)
{
var index = 0;
var firstToken = tokens[index++];
Expression<Func<T, bool>> subQuery = entity =>
stringProp(entity).Contains(firstToken);
for (; index < tokens.Length; index++)
{
var tempToken = tokens[index];
subQuery = subQuery.And(
entity => stringProp(entity).Contains(tempToken));
}
return subQuery;
}
I'd also be interested to hear if this all looks like a bad idea.

Here's what I use to do this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Collections.ObjectModel;
namespace MyLibrary.Extensions
{
/// <summary>Defines extension methods for building and working with Expressions.</summary>
public static class ExpressionExtensions
{
/// <summary>Ands the Expressions.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expressions">The Expression(s) to and.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> And<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
{
if (expressions.IsNullOrEmpty())
return null;
Expression<Func<T, bool>> finalExpression = expressions.First();
foreach (Expression<Func<T, bool>> e in expressions.Skip(1))
finalExpression = finalExpression.And(e);
return finalExpression;
}
/// <summary>Ors the Expressions.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expressions">The Expression(s) to or.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> Or<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
{
if (expressions.IsNullOrEmpty())
return null;
Expression<Func<T, bool>> finalExpression = expressions.First();
foreach (Expression<Func<T, bool>> e in expressions.Skip(1))
finalExpression = finalExpression.Or(e);
return finalExpression;
}
/// <summary>Ands the Expression with the provided Expression.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expression1">The left Expression to and.</param>
/// <param name="expression2">The right Expression to and.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
//Reuse the first expression's parameter
ParameterExpression param = expression1.Parameters.Single();
Expression left = expression1.Body;
Expression right = RebindParameter(expression2.Body, expression2.Parameters.Single(), param);
BinaryExpression body = Expression.AndAlso(left, right);
return Expression.Lambda<Func<T, bool>>(body, param);
}
/// <summary>Ors the Expression with the provided Expression.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expression1">The left Expression to or.</param>
/// <param name="expression2">The right Expression to or.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
//Reuse the first expression's parameter
ParameterExpression param = expression1.Parameters.Single();
Expression left = expression1.Body;
Expression right = RebindParameter(expression2.Body, expression2.Parameters.Single(), param);
BinaryExpression body = Expression.OrElse(left, right);
return Expression.Lambda<Func<T, bool>>(body, param);
}
/// <summary>Updates the supplied expression using the appropriate parameter.</summary>
/// <param name="expression">The expression to update.</param>
/// <param name="oldParameter">The original parameter of the expression.</param>
/// <param name="newParameter">The target parameter of the expression.</param>
/// <returns>The updated expression.</returns>
private static Expression RebindParameter(Expression expression, ParameterExpression oldParameter, ParameterExpression newParameter)
{
if (expression == null)
return null;
switch (expression.NodeType)
{
case ExpressionType.Parameter:
{
ParameterExpression parameterExpression = (ParameterExpression)expression;
return (parameterExpression.Name == oldParameter.Name ? newParameter : parameterExpression);
}
case ExpressionType.MemberAccess:
{
MemberExpression memberExpression = (MemberExpression)expression;
return memberExpression.Update(RebindParameter(memberExpression.Expression, oldParameter, newParameter));
}
case ExpressionType.AndAlso:
case ExpressionType.OrElse:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
{
BinaryExpression binaryExpression = (BinaryExpression)expression;
return binaryExpression.Update(RebindParameter(binaryExpression.Left, oldParameter, newParameter), binaryExpression.Conversion, RebindParameter(binaryExpression.Right, oldParameter, newParameter));
}
case ExpressionType.Call:
{
MethodCallExpression methodCallExpression = (MethodCallExpression)expression;
return methodCallExpression.Update(RebindParameter(methodCallExpression.Object, oldParameter, newParameter), methodCallExpression.Arguments.Select(arg => RebindParameter(arg, oldParameter, newParameter)));
}
case ExpressionType.Invoke:
{
InvocationExpression invocationExpression = (InvocationExpression)expression;
return invocationExpression.Update(RebindParameter(invocationExpression.Expression, oldParameter, newParameter), invocationExpression.Arguments.Select(arg => RebindParameter(arg, oldParameter, newParameter)));
}
default:
{
return expression;
}
}
}
public static Expression<Func<T, bool>> BuildContainsExpression<T, R>(Expression<Func<T, R>> valueSelector, IEnumerable<R> values)
{
if (null == valueSelector)
throw new ArgumentNullException("valueSelector");
if (null == values)
throw new ArgumentNullException("values");
ParameterExpression parameterExpression = valueSelector.Parameters.Single();
IEnumerable<BinaryExpression> equalExpressions = null;
Expression aggregationExpression = null;
if (!values.IsNullOrEmpty())
return (e => false);
equalExpressions = values.Select(v => Expression.Equal(valueSelector.Body, Expression.Constant(v, typeof(R))));
aggregationExpression = equalExpressions.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
return Expression.Lambda<Func<T, bool>>(aggregationExpression, parameterExpression);
}
public static Expression<Func<T, bool>> BuildDoesNotContainExpression<T, R>(Expression<Func<T, R>> valueSelector, IEnumerable<R> values)
{
if (null == valueSelector)
throw new ArgumentNullException("valueSelector");
ParameterExpression parameterExpression = valueSelector.Parameters.Single();
IEnumerable<BinaryExpression> notEqualExpressions = null;
Expression aggregationExpression = null;
if (!values.IsNullOrEmpty())
return (e => false);
notEqualExpressions = values.Select(v => Expression.NotEqual(valueSelector.Body, Expression.Constant(v, typeof(R))));
aggregationExpression = notEqualExpressions.Aggregate<Expression>((accumulate, equal) => Expression.And(accumulate, equal));
return Expression.Lambda<Func<T, bool>>(aggregationExpression, parameterExpression);
}
}
}
Usage
string query = "kill mockingbird";
string[] tokens = query.Split(' ');
Expression<Func<Book, string>> inClause = BuildContainsExpression<Book, string>(o => o.Title, tokens);
using (LibraryDataContext dataContext = new LibraryDataContext())
{
List<Book> matchingBooks = dataContext.Books.Where(inClause).ToList();
}
Results
This will find all books whose title contains the words "kill" or "mockingbird."

The answer Josh provided is awesome and helped me to get exactly what I want. It however tests for equality of each token (it is also more generic as equality can be tested against any type) as opposed to a string.Contains test. Here is a solution that gives a string.Contains result:
public static Expression<Func<T, bool>>
BuildTokenizedStringQuery<T>(string[] tokens,
Expression<Func<T, string>> stringPropertyAccessor)
{
ParameterExpression parameterExpression = stringPropertyAccessor.Parameters
.Single();
var index = 0;
var firstToken = tokens[index ++];
Expression<Func<string, bool>> contains =
aString => aString.Contains(firstToken);
var invocation = Expression.Invoke(contains, stringPropertyAccessor.Body);
Expression<Func<T, bool>> expression = Expression
.Lambda<Func<T, bool>>(invocation, parameterExpression);
for (; index < tokens.Length; index++)
{
var tempToken = tokens[index];
contains = aString => aString.Contains(tempToken);
invocation = Expression.Invoke(contains, stringPropertyAccessor.Body);
expression = expression.And(Expression
.Lambda<Func<T, bool>>(invocation, parameterExpression));
}
return expression;
}

Related

EF Core, append to predicate builder in Any condition

I'm trying to merge two predicates in then Any clause of the following code, but i cannot find a way to do this.
private static Expression<Func<Order, bool>> BuildWhereExpression(DataFilterOrder filter, AppDbContext dbContext)
{
var predicate = PredicateBuilder.True<Order>();
var confirmationPredicate = PredicateBuilder.True<HConfirmation>();
if (!string.IsNullOrWhiteSpace(filter.ConfirmationNumber))
{
confirmationPredicate = confirmationPredicate.And(r =>
r.Confirmation.Document.Number == filter.ConfirmationNumber);
}
if (filter.ConfirmationDateFrom != null)
{
confirmationPredicate = confirmationPredicate.And(r =>
r.Confirmation.Document.Date >= filter.ConfirmationDateFrom);
}
.....
predicate = predicate.And(o =>
dbContext.Confirmations
.Join(
dbContext.DocumentHierarchies,
c => c.DocumentId,
h => h.ChildDocumentId,
(c, h) => new HConfirmation { Confirmation = c, Hierarchy = h })
.Any(r => r.Hierarchy.ParentDocumentId == o.DocumentId &&
???confirmationPredicate???)
return predicate;
}
....
// called by
var wherePredicate = BuildWhereExpression(filter, dbContext);
var list = await dbContext.Orders
.Where(wherePredicate)
.ToListAsync();
Any help? Thanks very much.
PredicateBuilder class:
public static class PredicateBuilder
{
// Creates a predicate that evaluates to true.
public static Expression<Func<T, bool>> True<T>() { return param => true; }
// Creates a predicate that evaluates to false.
public static Expression<Func<T, bool>> False<T>() { return param => false; }
// Creates a predicate expression from the specified lambda expression.
public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
public static Expression<Func<T, bool>> Create1<T, K>(Expression<Func<T, bool>> predicate, K obj) { return predicate; }
// Combines the first predicate with the second using the logical "and".
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
// Combines the first predicate with the second using the logical "or".
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
// Negates the predicate.
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
{
var negated = Expression.Not(expression.Body);
return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
}
// Combines the first expression with the second using the specified merge function.
static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
// zip parameters (map from parameters of second to parameters of first)
var map = first.Parameters
.Select((f, i) => new { f, s = second.Parameters[i] })
.ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with the parameters in the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// create a merged lambda expression with parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
class ParameterRebinder : ExpressionVisitor
{
readonly Dictionary<ParameterExpression, ParameterExpression> map;
ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}
}
Well, tried to simplify solution, but looks like it is needed to build Any part dynamically. Sorry not tested and some small mistakes can be here.
private static Expression<Func<Order, bool>> BuildWhereExpression(DataFilterOrder filter, AppDbContext dbContext)
{
var predicate = PredicateBuilder.True<Order>();
var confirmationPredicate = PredicateBuilder.True<HConfirmation>();
if (!string.IsNullOrWhiteSpace(filter.ConfirmationNumber))
{
confirmationPredicate = confirmationPredicate.And(r =>
r.Confirmation.Document.Number == filter.ConfirmationNumber);
}
if (filter.ConfirmationDateFrom != null)
{
confirmationPredicate = confirmationPredicate.And(r =>
r.Confirmation.Document.Date >= filter.ConfirmationDateFrom);
}
.....
// we can write this query separately
var confirmations = dbContext.Confirmations
.Join(dbContext.DocumentHierarchies,
c => c.DocumentId,
h => h.ChildDocumentId,
(c, h) => new HConfirmation { Confirmation = c, Hierarchy = h }
);
var orderParam = Expression.Parameter(typeof(Order), "o");
var hConfirmationParam = Expression.Parameter(typeof(HConfirmation), "r");
// r.Hierarchy.ParentDocumentId == o.DocumentId
var anyPredicate = (Expression)Expression.Equal(Expression.Property(Expression.Property(hConfirmationParam, "Hierarchy"), "ParentDocumentId"),
Expression.Property(orderParam, "DocumentId"));
// r.Confirmation
var confirmationAccess = Expression.Property(hConfirmationParam, "Confirmation");
// correcting confirmation predicate
var confirmationPredicateCorrected = ExpressionReplacer.GetBody(confirmationPredicate, confirmationAccess);
// r.Hierarchy.ParentDocumentId == o.DocumentId && confirmationPredicateCorrected
anyPredicate = Expression.AndAlso(anyPredicate, confirmationPredicateCorrected);
// r => r.Hierarchy.ParentDocumentId == o.DocumentId && confirmationPredicateCorrected
var anyLambda = Expression.Lambda(anyPredicate, hConfirmationParam);
var anyCall = Expression.Call(typeof(Queryable), "Any", new [] { typeof(HConfirmation) }, confirmations.Expression, Expression.Quote(anyLambda));
var additionalPredicate = Expression.Lambda<Func<Order, bool>>(anyCall, orderParam);
predicate = predicate.And(additionalPredicate);
return predicate;
}
Anyawy, additional helper class is needed:
public class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> { { toReplace, toExpr } }).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
.ToDictionary(i => (Expression) lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
}
}
Strongly recommend this extension for Expression Tree visualization and debugging: https://marketplace.visualstudio.com/items?itemName=vs-publisher-1232914.ReadableExpressionsVisualizers

Ravendb Session.Query<T>() method does not return any result when sending Expression<Func<T, bool>> to Where() Extension method

I've got a problem with Ravendb session.Query.Where().
When I use it like this it works perfectly and returns all of UserIdentitiy with User name 'John' :
users = session.Query<UserIdentity>().Where(x => x.UserName == "John").ToList();
But when I try to send the Expression as a variable to Where method it fails silently and returns nothing.
Expression<Func<UserIdentity, bool>> whereClause = x => x.UserName == "John";
using (var session = _store.OpenSession())
{
users = session.Query<UserIdentity>().Where(whereClause).ToList();
}
What's wrong?
Try the following code:
public static class ExpressionTreesExtension
{
public static Expression<Func<T, bool>> EqualExpression<T>(string columnName,object value)
{
ParameterExpression parameterType = Expression.Parameter(typeof(T), "object");
MemberExpression typeColumn = Expression.Property(parameterType, columnName);
ConstantExpression constant = Expression.Constant(value, typeof(string));
BinaryExpression binaryExpression = Expression.Equal(typeColumn, constant);
return Expression.Lambda<Func<T, bool>>(binaryExpression, parameterType);
}
}
Code Usage:
Expression<Func<UserIdentity, bool>> whereClause = ExpressionTreesExtension.EqualExpression<UserIdentity>("UserName","John");

Generating lambda expression - Type conversion Error

I have a generic method that filters a list of entities, filtering is done by generating lambda expressions:
protected object initFilters<TEntity>(string targetEntity, List<SearchItem> searchItems, int page_size = 20, int offset = 0 , ExpressionSortCriteria<TEntity, string> SortCriteria)
{
var SearchQuery = new SearchQuery<TEntity>();
if (searchItems != null)
{
var predicat = ExpressionBuilder.GetExpression<TEntity>(searchItems).Compile();
SearchQuery.AddFilter(predicat);
}
//SearchQuery.AddSortCriteria(new ExpressionSortCriteria<Profile, string> { SortExpression = (profile => profile.Libelle), Direction = SortDirection.Ascending });
SearchQuery.Skip = offset;
SearchQuery.Take = page_size;
return (object)SearchQuery;
}
My SearchQuery contains criteria that will be applied to the list.
My predicate is build by a method in this way :
public static Expression<Func<T, bool>> GetExpression<T>(IList<SearchItem> filters)
{
some code ...
return Expression.Lambda<Func<T, bool>>(exp, param);
}
My AddFilter (in the searchQuery) is like this :
public void AddFilter(Expression<Func<TEntity, Boolean>> filter)
{
Filters.Add(filter);
}
I have a problem with this line :
SearchQuery.AddFilter(predicat);
Error:
cannot convert from System.Func to System.Linq.Expressions.Expression>
As you see , my two methods are using
Expression<Func<T, bool>>
Any idea please how to solve this ?
in your comment, you say you make
var SearchQuery = new SearchQuery<TEntity>();
if (searchItems != null) {
var predicat = ExpressionBuilder.GetExpression<TEntity>(searchItems).Compile();
SearchQuery.AddFilter(predicat);
}
of course, if your compile your Expression, it's no more an Expression<Func<T, bool>> but a Func<T, bool>
and AddFilter takes an Expression<Func<T, bool>> as argument.
So... remove the Compile()

LLBLGen TypedConstantExpression can't be converted to SetExpression

We were experiencing issues with using over 2100 elements in a contains LINQ expression, so I have rewritten the query to insert the values previously used in the contains comparison into an IEnumerable of structs of type EnquiryID exposing a simple int value (ID) and joined on this:
IEnumerable<EnquiryID> enqIdList = ListToEnquiryIdList(enquiryIDs).ToList();
var extras = (from q in lmd.Quote
join qe in lmd.QuoteExtra on q.Id equals qe.QuoteId
join ei in enqIdList on q.EnquiryId equals ei.Id
orderby qe.Created
select new
{
EnquiryID = q.EnquiryId, qe.Created
}).ToArray();
This generates this exception:
"Unable to cast object of type 'System.Linq.Expressions.TypedConstantExpression' to type 'SD.LLBLGen.Pro.LinqSupportClasses.ExpressionClasses.SetExpression'.", which is clearly LLBLGEN specific
Anyone have any ideas?
You can access the 'Value' property of the TypedConstantExpression like so:
(expression.Body as MethodCallExpression).Object.GetType().GetProperty("Value").GetMethod.Invoke((expression.Body as MethodCallExpression).Object, null)
Where the expression is given by a LambdaExpression ()=>(null).GetType()
A Complete example
static readonly Type TypedConstantExpressionType = Type.GetType("System.Linq.Expressions.TypedConstantExpression, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
static readonly PropertyInfo TypedConstantExpressionValueProperty;
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized | System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
static SymbolExtensions()
{
TypedConstantExpressionValueProperty = IntrospectionExtensions.GetTypeInfo(TypedConstantExpressionType).GetProperty("Value");
}
/// <summary>
/// Given a lambda expression that expressed a new object, returns the <see cref="System.Reflection.TypeInfo"/> of what type was expected to be allocated
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
public static TypeInfo GetTypeInfo(Expression<Action> expression) //Expression<Action> allows the syntax () => where Expression would require a Delgate.
{
Expression body = expression.Body;
if (body is NewExpression)
{
NewExpression newExpression = expression.Body as NewExpression;
return IntrospectionExtensions.GetTypeInfo((expression.Body as NewExpression).Constructor.DeclaringType);
}
else if (body is MemberExpression)
{
MemberExpression memberExpression = body as MemberExpression;
return IntrospectionExtensions.GetTypeInfo(memberExpression.Member.DeclaringType);
}
else if (body is MethodCallExpression)
{
MethodCallExpression methodCallExpression = expression.Body as MethodCallExpression;
if (methodCallExpression.Object is MemberExpression)
{
return IntrospectionExtensions.GetTypeInfo((methodCallExpression.Object as MemberExpression).Member.DeclaringType);
}
//Actually a RuntimeType from a TypedConstantExpression...
return IntrospectionExtensions.GetTypeInfo((Type)TypedConstantExpressionValueProperty.GetMethod.Invoke(methodCallExpression.Object, null));
}
throw new System.NotSupportedException("Please create an issue for your use case.");
}
Tested with the following class and code:
public class MyTestClass
{
string m_Test;
public string Test
{
get { return m_Test; }
set { m_Test = value; }
}
}
public class MyTestClass<T> : MyTestClass
{
T Backing;
public T Property
{
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
get { return Backing; }
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
set { Backing = value; }
}
public T AnotherProperty
{
get;
set;
}
}
System.Reflection.TypeInfo typeInfo = Media.Common.Extensions.ExpressionExtensions.SymbolExtensions.GetTypeInfo(() => new MyTestClass<int>());
if (typeInfo.GetGenericArguments()[0] != typeof(int)) throw new System.Exception("Not correct type");
System.Console.WriteLine("TypeInfo.Name" + typeInfo.Name);
System.Console.WriteLine("TypeInfo.MetadataToken" + typeInfo.MetadataToken);
if (typeInfo.GetGenericArguments()[0] != typeof(int)) throw new System.Exception("Not correct type");
typeInfo = Media.Common.Extensions.ExpressionExtensions.SymbolExtensions.GetTypeInfo(() => (typeof(MyTestClass<int>)).GetType());
if (typeInfo.GetGenericArguments()[0] != typeof(int)) throw new System.Exception("Not correct type");
System.Type unboundedType = typeof(MyTestClass<>);
typeInfo = Media.Common.Extensions.ExpressionExtensions.SymbolExtensions.GetTypeInfo(() => (unboundedType).GetType());
System.Console.WriteLine("TypeInfo.Name" + typeInfo.Name);
System.Console.WriteLine("TypeInfo.MetadataToken" + typeInfo.MetadataToken);

Assignment in .NET 3.5 expression trees

Is it possible to encode an assignment into an expression tree?
No, I don't believe so.
Certainly the C# compiler disallows it when converting a lambda expression:
int x;
Expression<Func<int,int>> foo = (x=y); // Assign to x and return value
This yields the error:
CS0832: An expression tree may not contain an assignment operator
You should able to do it with .NET 4.0 Library. by import Microsoft.Scripting.Core.dll to your .NET 3.5 project.
I am using DLR 0.9 - There might be some change on Expession.Block and Expression.Scope in version 1.0 (You can see reference from http://www.codeplex.com/dlr/Thread/View.aspx?ThreadId=43234)
Following sample is to show you.
using System;
using System.Collections.Generic;
using Microsoft.Scripting.Ast;
using Microsoft.Linq.Expressions;
using System.Reflection;
namespace dlr_sample
{
class Program
{
static void Main(string[] args)
{
List<Expression> statements = new List<Expression>();
ParameterExpression x = Expression.Variable(typeof(int), "r");
ParameterExpression y = Expression.Variable(typeof(int), "y");
statements.Add(
Expression.Assign(
x,
Expression.Constant(1)
)
);
statements.Add(
Expression.Assign(
y,
x
)
);
MethodInfo cw = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) });
statements.Add(
Expression.Call(
cw,
y
)
);
LambdaExpression lambda = Expression.Lambda(Expression.Scope(Expression.Block(statements), x, y));
lambda.Compile().DynamicInvoke();
Console.ReadLine();
}
}
}
My extension method for doing exactly this:
/// <summary>
/// Provides extensions for converting lambda functions into assignment actions
/// </summary>
public static class ExpressionExtenstions
{
/// <summary>
/// Converts a field/property retrieve expression into a field/property assign expression
/// </summary>
/// <typeparam name="TInstance">The type of the instance.</typeparam>
/// <typeparam name="TProp">The type of the prop.</typeparam>
/// <param name="fieldGetter">The field getter.</param>
/// <returns></returns>
public static Expression<Action<TInstance, TProp>> ToFieldAssignExpression<TInstance, TProp>
(
this Expression<Func<TInstance, TProp>> fieldGetter
)
{
if (fieldGetter == null)
throw new ArgumentNullException("fieldGetter");
if (fieldGetter.Parameters.Count != 1 || !(fieldGetter.Body is MemberExpression))
throw new ArgumentException(
#"Input expression must be a single parameter field getter, e.g. g => g._fieldToSet or function(g) g._fieldToSet");
var parms = new[]
{
fieldGetter.Parameters[0],
Expression.Parameter(typeof (TProp), "value")
};
Expression body = Expression.Call(AssignmentHelper<TProp>.MethodInfoSetValue,
new[] {fieldGetter.Body, parms[1]});
return Expression.Lambda<Action<TInstance, TProp>>(body, parms);
}
public static Action<TInstance, TProp> ToFieldAssignment<TInstance, TProp>
(
this Expression<Func<TInstance, TProp>> fieldGetter
)
{
return fieldGetter.ToFieldAssignExpression().Compile();
}
#region Nested type: AssignmentHelper
private class AssignmentHelper<T>
{
internal static readonly MethodInfo MethodInfoSetValue =
typeof (AssignmentHelper<T>).GetMethod("SetValue", BindingFlags.NonPublic | BindingFlags.Static);
private static void SetValue(ref T target, T value)
{
target = value;
}
}
#endregion
}
As Jon Skeet and TraumaPony have already said, Expression.Assign isn't available before .NET 4. Here's another concrete example of how to work around this missing bit:
public static class AssignmentExpression
{
public static Expression Create(Expression left, Expression right)
{
return
Expression.Call(
null,
typeof(AssignmentExpression)
.GetMethod("AssignTo", BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(left.Type),
left,
right);
}
private static void AssignTo<T>(ref T left, T right) // note the 'ref', which is
{ // important when assigning
left = right; // to value types!
}
}
Then simply call AssignmentExpression.Create() in place of Expression.Assign().
You could probably work around it by nexting expression trees. Call a lambda function, where an argument is the value of the assignee.

Resources