how to extracting a part of an expression - c#

i want to simplify code to add loadoptions and association filtering so i created this class.
class GraphQuery<T>
{
private IQueryable<T> query;
private DataLoadOptions load;
public GraphQuery(DataLoadOptions load, IQueryable<T> query)
{
this.load = load;
this.query = query;
}
public GraphQuery<T> Load(
Expression<Func<T, object>> expr,
Expression<Func<T, object>> filter)
{
load.LoadWith(expr);
load.AssociateWith(filter);
return this;
}
// more public methods ...
}
it can then be used like this:
var clients = Graph(db.Clients.Where(e => !e.Deleted))
.Load(e => e.ClientPersons,
e => e.ClientPersons.Where(j => !j.Person.Deleted));
however, i see a very simple repetition of e => e.ClientPersons. so i would like to reduce the above usage to :
var clients = Graph(db.Clients.Where(e => !e.Deleted))
.Load(e => e.ClientPersons.Where(j => !j.Person.Deleted));
so the Load function should look something like
public GraphQuery<T> Load(Expression<Func<T, object>> filter)
{
var expr = ... extract first part of the expression that represents the association property
load.LoadWith(expr);
load.AssociateWith(filter);
return this;
}
i have never worked with linq expressions except for using them in queries

i used the debugger to see what i could get from the expression and found it rather simple to extract what i needed. i guess its not pretty, but it gets the job done. if anybody has some recommendations to improve this - please post a comment.
private static LambdaExpression GetRootMemberExpression(LambdaExpression lambda1)
{
var expr = lambda1.Body;
while (!FindRoot(ref expr)) ;
if (!(expr is MemberExpression))
throw new Exception("MemberExpression required");
return Expression.Lambda(expr, (expr as MemberExpression).Expression as ParameterExpression);
}
private static bool FindRoot(ref Expression expr)
{
if (expr is MemberExpression)
return FindRoot(ref expr, (expr as MemberExpression).Expression);
if (expr.NodeType == ExpressionType.Call)
return FindRoot(ref expr, (expr as MethodCallExpression).Object);
throw new Exception("Unexpected Expression type encountered (" + expr.NodeType + ")");
}
private static bool FindRoot(ref Expression expr, Expression expr2)
{
if (expr2.NodeType == ExpressionType.Parameter)
return true;
expr = expr2;
return false;
}

Related

FilterByItems Method not working correctly

Im building a filter and im using the FilterByItems method to compare two Arrays(one from my frontend and one from my database). It all goes through a Overlay Panel in my Frontend, where the user can select different persontypes like member, worker and much more. So my problem is now that i got multiple persontype hovers which dont want do work together. If i just have one it works fine, if i add a second one it only works if both have the same values in the frontend list.
The right case would be:
Frontend Panel one got the Member in the Array, Frontend Panel two got the Friend in the Array.
Table shows all persons who are saved with these types in their profile.
The current and wrong case is:
Panel 1 and Panel two are not working with different arrays and only showing informations if both have the same list for example both got member on position [0]
Generelly it seems like it works as a single one as an and query and as the second panel joins in it blocks completely. So here is my code(FilterByItems Method is linked above):
//The Filter
//Login
[HttpPost("filter/")]
public async Task<IActionResult> Filter([FromBody] Filter user)
{
var baseQuery = _context.Personens.AsQueryable();
//Personentyp 1 Dont works in combination with below
if (user.personenTypFilter.Length > 0)
baseQuery = baseQuery.FilterByItems(user.personenTypFilter, (m, k) => m.Personentypzuordnungens.Any(i => i.Personentyp.Bezeichnung.Contains(k)), true);
////Personentyp 2
//if (user.personenTypFilter2.Length > 0)
// baseQuery = baseQuery.FilterByItems(user.personenTypFilter2, (m, k) => m.Personentypzuordnungens.Any(i => i.Personentyp.Bezeichnung.Contains(k)), true);
//---------------------------------
var result = await (baseQuery.Select(p => new
{
personId = p.PersonId,
nachname = p.Nachname,
vorname = p.Vorname,
plz = p.Plz,
firmBez = p.Firmenbezeichnung,
ort = p.Ort,
personentyp = p.Personentypzuordnungens.Select(i => new
{
personentypId = i.PersonentypId,
}),
aktuellePosition = p.AktuellePosition,
taetigkeit = p.Tätigkeit,
kernkompetenzen = p.Kernkompetenzen,
datenReviewedZeitpunkt = p.DatenReviewedZeitpunkt,
}).ToListAsync());
return Ok(result);
}
Thats how i declared my variables in my Filter Model:
public string[] personenTypFilter { get; set; }
public string[] personenTypFilter2 { get; set; }
New problem with .CombineAnd(from comments)
baseQuery = baseQuery.Where(
//Characteristics
character1Predicate.CombineOr(character1Predicate2).CombineOr(character1Predicate3)
//Persontypes
.CombineAnd(personType1Predicate.CombineOr(personType2Predicate).CombineOr(personType3Predicate)));
This function is evolution of FilterByItems and adds additional public methods for generating predicate and combining them.
You can use new extensions in your query in the following way:
var baseQuery = _context.Personens.AsQueryable();
var predicate1 = baseQuery.GetItemsPredicate(ser.personenTypFilter, (m, k) => m.Personentypzuordnungens.Any(i => i.Personentyp.Bezeichnung.Contains(k)));
var predicate2 = baseQuery.GetItemsPredicate(user.personenTypFilter2, (m, k) => m.Personentypzuordnungens.Any(i => i.Personentyp.Bezeichnung.Contains(k)));
// filter by combined predicates
baseQuery = baseQuery.Where(predicate1.CombineOr(predicate2));
And implementation:
public static class QueryableExtensions
{
public static IQueryable<T> FilterByItems<T, TItem>(this IQueryable<T> query, IEnumerable<TItem> items,
Expression<Func<T, TItem, bool>> filterPattern, bool isOr = true, bool emptyValue = true)
{
var filterLambda = query.GetItemsPredicate(items, filterPattern, isOr, emptyValue);
return query.Where(filterLambda);
}
public static Expression<Func<T, bool>> GetItemsPredicate<T, TItem>(this IEnumerable<T> query, IEnumerable<TItem> items, Expression<Func<T, TItem, bool>> filterPattern, bool isOr = true, bool emptyValue = false)
{
Expression predicate = null;
foreach (var item in items)
{
var itemExpr = Expression.Constant(item);
var itemCondition = ExpressionReplacer.Replace(filterPattern.Body, filterPattern.Parameters[1], itemExpr);
if (predicate == null)
predicate = itemCondition;
else
{
predicate = Expression.MakeBinary(isOr ? ExpressionType.OrElse : ExpressionType.AndAlso, predicate,
itemCondition);
}
}
predicate ??= Expression.Constant(emptyValue);
var filterLambda = Expression.Lambda<Func<T, bool>>(predicate, filterPattern.Parameters[0]);
return filterLambda;
}
public static Expression<Func<T, bool>> CombineOr<T>(this Expression<Func<T, bool>> predicate1,
Expression<Func<T, bool>> predicate2)
{
var parameter = predicate1.Parameters[0];
var body = Expression.OrElse(predicate1.Body, ExpressionReplacer.GetBody(predicate2, parameter));
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
public static Expression<Func<T, bool>> CombineAnd<T>(this Expression<Func<T, bool>> predicate1,
Expression<Func<T, bool>> predicate2)
{
var parameter = predicate1.Parameters[0];
var body = Expression.AndAlso(predicate1.Body, ExpressionReplacer.GetBody(predicate2, parameter));
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
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 node)
{
if (node != null && _replaceMap.TryGetValue(node, out var replacement))
return replacement;
return base.Visit(node);
}
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);
}
}
}

Expression parameter is not defined

I am trying to perform a query against a list to give immediate results using an expression that is set elsewhere in the code, while a second thread goes off and uses it to get a full set of results from a database in a Linq query.
I know that the expression itself is OK as when I send it over the wire to the server side and apply it against an IQueryable then it will work. However, when applied on the client side the following error is produced:
System.InvalidOperationException: 'variable 'item' of type 'MissionControlSuite.Shared.Interface.Model.IBox' referenced from scope '', but it is not defined'
This code that performs the filtering:
public abstract class GridWithPaging<T> where T : class
{
List<T> _items
public Expression<Func<T, bool>> Filter { get; set; }
public ObservableCollection<IGridFilter<T>> Filters { get; set; }
// more code skipped for breveity
public void FiltersChanged(object sender, EventArgs e)
{
Expression<Func<T, bool>> combined = item => true;
foreach (var filter in Filters)
combined = Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(filter.Expression.Body, combined.Body),
combined.Parameters.Single());
Filter = combined;
NotifyPropertyChanged("Filter");
}
public void AddFilter(IGridFilter<T> filter)
{
Filters.Add(filter);
NotifyPropertyChanged("Filters");
}
private void DoFiltering(int start)
{
var newView = _items.Skip(start);
if(Filter != null)
newView = newView.AsQueryable().Where(Filter);
//some more code that acts on the filtered list
}
}
The expression is set elsewhere like this:
Expression<Func<IBox, bool>> expression = item => item.BoxId.Contains(v);
var filter = new GridFilter<IBox>()
{
Name = string.Format("{0} contains {1}", Property, Value),
Expression = expression
};
Grid.AddFilter(filter);
This answer will help you: Combining two expressions (Expression<Func<T, bool>>)
To summarize: The problem is that the parameters, although with the same name, are not the same instance of the ParameterExpression - which is included in the comment to that answer.
The solution is to use a Visitor (code copied over from linked post):
public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
Also note: the ORM provider (or another IQueryable you use) probably works because it translates that to text directly, whereas executing (Invokeing) this LambdaExpression causes the error as it's more strict, in a way. Some IQueryables can also accept simple string parameters and parse that on their own, which should indicate that they have a wider range of acceptable inputs.

Combining multiple expressions trees

I'm getting the following error
The parameter 'p' was not bound in the specified LINQ to Entities
query expression.
I understand the problem (same instance of ParameterExpression should be used with all the expressions in the tree) and have attempted to use solutions I've found online but with no luck.
This is my method
private void SeedEntity<TEntity>(DatabaseContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions) where TEntity : class
{
Expression<Func<TEntity, bool>> allExpresions = null;
var parameters = identifierExpressions.SelectMany(x => x.Parameters).GroupBy(x => x.Name).Select(p => p.First()).ToList();
foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions)
{
Func<TEntity, object> vv = identifierExpression.Compile();
object constant = vv(entity);
ConstantExpression constExp = Expression.Constant(constant, typeof(object));
BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp);
Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, parameters);
if (allExpresions == null)
{
allExpresions = equalExpression2;
}
else
{
BinaryExpression bin = Expression.And(allExpresions.Body, equalExpression2.Body);
allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, parameters);
}
}
TEntity existingEntity = null;
if (allExpresions != null)
{
existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions);
}
if (existingEntity == null)
{
context.Set<TEntity>().Add(entity);
}
else
{
entity = existingEntity;
}
}
It generates an expression for the lookup of an entity based on a number of properties.
It works fine for a single expression, the error only occurs when passing in multiple.
Called like this:
SeedEntity(context, ref e, p=> p.Name);//Works
SeedEntity(context, ref e, p=> p.Name, p=> p.Age);//Fails
It generates something similar to me performing the following:
context.Set<TEntity>().FirstOrDefault(p=>p.Name == e.Name && p.Age == e.Age);
Replacing e.Name && e.Age with a ConstantExpression
You can see in the method above I grab all of the unique params and store them in parameters at the top, then use the same variable throughout.This is the start, but then I need to replace the instances of the parameter in each of the Expression<Func<TEntity, bool>> passed in as the params array, this is where I'm failing.
I've tried enumerate the expressions and use the .Update() method passing in the params
I also tried a solution using the ExpressionVisitor
public class ExpressionSubstitute : ExpressionVisitor
{
public readonly Expression from, to;
public ExpressionSubstitute(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
if (node == from) return to;
return base.Visit(node);
}
}
public static class ExpressionSubstituteExtentions
{
public static Expression<Func<TEntity, TReturnType>> RewireLambdaExpression<TEntity, TReturnType>(Expression<Func<TEntity, TReturnType>> expression, ParameterExpression newLambdaParameter)
{
var newExp = new ExpressionSubstitute(expression.Parameters.Single(), newLambdaParameter).Visit(expression);
return (Expression<Func<TEntity, TReturnType>>)newExp;
}
}
You're really close. I don't see the point of your parameters variable. Grouping them by name is a mistake. Why not just pass the parameters from the expression? Then visit if necessary. Your visitor code is fine.
private static void SeedEntity<TEntity>(DbContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions)
where TEntity : class
{
Expression<Func<TEntity, bool>> allExpresions = null;
foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions)
{
Func<TEntity, object> vv = identifierExpression.Compile();
object constant = vv(entity);
ConstantExpression constExp = Expression.Constant(constant, typeof(object));
BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp);
Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, identifierExpression.Parameters);
if (allExpresions == null)
{
allExpresions = equalExpression2;
}
else
{
var visitor = new ExpressionSubstitute(allExpresions.Parameters[0], identifierExpression.Parameters[0]);
var modifiedAll = (Expression<Func<TEntity,bool>>)visitor.Visit(allExpresions);
BinaryExpression bin = Expression.And(modifiedAll.Body, equalExpression2.Body);
allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, identifierExpression.Parameters);
}
}
TEntity existingEntity = null;
if (allExpresions != null)
{
existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions);
}
if (existingEntity == null)
{
context.Set<TEntity>().Add(entity);
}
else
{
entity = existingEntity;
}
}

Generic method for querying Entity Framework

I would like to have generic method for filtering Entity Framework IQueryable<TItem> by IEnumerable<TEnum>. Its signature should probably look like this:
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TItem, IEnumerable<TEnum>, bool>> predicate)
{
return items.Where(??????);
}
and I would want to be able to call it for example like this:
IQueryable<Request> requests = service.GetAllRequests();
IEnumerable<RequestState> states = new RequestState[] {RequestState.Active, RequestState.Closed};
Expression<Func<Request, IEnumerable<RequestState>, bool>> predicate = (r, s) => s.Contains(r.State);
requests = ApplyFilter(requests, states, predicate);
But what should be inside method's body? How can I convert Expression<Func<TItem, IEnumerable<TEnum>, bool>> to Expression<Func<TItem, bool>> for use as parameter to "Where" method? Will it work with Entity Framework?
IMO, the predicate should be inside your ApplyFilter method (respect of concerns).
One possible way to code this would be :
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items,
IEnumerable<TEnum> enumValues, Expression<Func<TItem,TEnum>> enumField)
{
return items.Where(i => enumValues.Contains(enumField(i)));
}
with a call like this :
requests = ApplyFilter(requests, states, r => r.State);
When I was implementing Sharped's answer I found that it is actually possible to do exactly what I want. The point is to use custom class that inherits from ExpressionVisitor. This class will replace every occurrence of IEnumerable<TEnum> parameter within expression tree with its actual value. My final code:
public static IQueryable<TFilteredItem> ApplyFilter<TFilteredItem, TEnum>(IQueryable<TFilteredItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TFilteredItem, IEnumerable<TEnum>, bool>> predicate)
{
ParameterExpression itemParam = predicate.Parameters[0];
ParameterExpression enumsParam = predicate.Parameters[1];
var em = new ExpressionModifier<IEnumerable<TEnum>>(enumsParam.Name, enumValues);
Expression predicateBody = em.Modify(predicate.Body);
return items.Where(Expression.Lambda<Func<TFilteredItem, bool>>(predicateBody, new[] { itemParam }));
}
public class ExpressionModifier<T> : ExpressionVisitor
{
private readonly string parameterName;
private readonly T newValue;
public Expression Modify(Expression expression)
{
return Visit(expression);
}
public ExpressionModifier(string parameterName, T newValue)
{
this.parameterName = parameterName;
this.newValue = newValue;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Name == this.parameterName ? Expression.Constant(this.newValue, node.Type) : base.VisitParameter(node);
}
}
And I am using it like:
var requests = this.ServiceManager.Messaging.GetRequests();
var states = new[] {RequestState.Active};
requests = ApplyFilter(requests, states, (i, e) => e.Contains(i.State));

LINQ Filter Implementation with Expressions

In MVC4 , I am providing Search box to the user to search any value in Table.
So I am implementing Generic Filter Condition at server side in C#
Need a help to combine multiple expressions to form single expression
Expression<Func<T, bool>>
For Example
Table Columns
MenuText, Role Name (Role.Name mapping), ActionName
Now If user entered in search box for ABC , which can be in any of the rows in shown columns, need to filter.
Model
public class Menu
{
public string MenuText {get;set;}
public Role Role {get;set;}
public string ActionName {get;set;}
}
public class Role
{
public string Name {get;set;}
}
So far I have implemented
/// <summary>
/// string[] properties property.Name (MenuText, ActionName), including deeper Mapping names such as (Role.Name)
/// </summary>
public static Expression<Func<T, bool>> FilterKey<T>(string filterText, params string[] properties)
{
ParameterExpression parameter = Expression.Parameter(typeof (T));
Expression[] propertyExpressions = properties.Select(
x => !string.IsNullOrEmpty(x) ? GetDeepPropertyExpression(parameter, x) : null).ToArray();
Expression<Func<T, bool>> predicate = PredicateBuilder.False<T>();
foreach (Expression expression in propertyExpressions)
{
var toLower = Expression.Call(expression, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
var like = Expression.Call(toLower, typeof(string).GetMethod("Contains"), Expression.Constant(filterText.ToLower()));
//TODO: Combine expressions to form single Expression<Func<T, bool>> expression
}
return predicate;
}
/// <summary>
/// To Get Deeper Properties such as Role.Name Expressions
/// </summary>
private static Expression GetDeepPropertyExpression(Expression initialInstance, string property)
{
Expression result = null;
foreach (string propertyName in property.Split('.'))
{
Expression instance = result ?? initialInstance;
result = Expression.Property(instance, propertyName);
}
return result;
}
I have created a few search IQueryable extension methods that you should be able to use
Full blog post is here:
http://jnye.co/Posts/6/c%23-generic-search-extension-method-for-iqueryable
GitHub project is here (has a couple of extra extensions for OR searches:
https://github.com/ninjanye/SearchExtensions
public static class QueryableExtensions
{
public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, string searchTerm)
{
if (String.IsNullOrEmpty(searchTerm))
{
return source;
}
// The below represents the following lamda:
// source.Where(x => x.[property] != null
// && x.[property].Contains(searchTerm))
//Create expression to represent x.[property] != null
var isNotNullExpression = Expression.NotEqual(stringProperty.Body, Expression.Constant(null));
//Create expression to represent x.[property].Contains(searchTerm)
var searchTermExpression = Expression.Constant(searchTerm);
var checkContainsExpression = Expression.Call(stringProperty.Body, typeof(string).GetMethod("Contains"), searchTermExpression);
//Join not null and contains expressions
var notNullAndContainsExpression = Expression.AndAlso(isNotNullExpression, checkContainsExpression);
var methodCallExpression = Expression.Call(typeof(Queryable),
"Where",
new Type[] { source.ElementType },
source.Expression,
Expression.Lambda<Func<T, bool>>(notNullAndContainsExpression, stringProperty.Parameters));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
}
This allows you to write something like:
string searchTerm = "test";
var results = context.Menu.Search(menu => menu.MenuText, searchTerm).ToList();
//OR for Role name
string searchTerm = "test";
var results = context.Menu.Search(menu => menu.Role.Name, searchTerm).ToList();
You might also find the following posts useful:
Search extension method that allows search accros multiple properties:
http://jnye.co/Posts/7/generic-iqueryable-or-search-on-multiple-properties-using-expression-trees
Search extension method that allows multiple or search terms on a property:
http://jnye.co/Posts/8/generic-iqueryable-or-search-for-multiple-search-terms-using-expression-trees
Thanks to NinjaNye , I have borrowed BuildOrExpression which resolved my problem
Here is the solution
public static Expression<Func<T, bool>> FilterKey<T>(string filterText, params string[] properties)
{
ParameterExpression parameter = Expression.Parameter(typeof (T));
Expression[] propertyExpressions = properties.Select(
x => !string.IsNullOrEmpty(x) ? GetDeepPropertyExpression(parameter, x) : null).ToArray();
Expression like= propertyExpressions.Select(expression => Expression.Call(expression, typeof (string).GetMethod("ToLower", Type.EmptyTypes))).Select(toLower => Expression.Call(toLower, typeof (string).GetMethod("Contains"), Expression.Constant(filterText.ToLower()))).Aggregate<MethodCallExpression, Expression>(null, (current, ex) => BuildOrExpression(current, ex));
return Expression.Lambda<Func<T, bool>>(like, parameter);
}
private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd)
{
if (existingExpression == null)
{
return expressionToAdd;
}
//Build 'OR' expression for each property
return Expression.OrElse(existingExpression, expressionToAdd);
}
private static Expression GetDeepPropertyExpression(Expression initialInstance, string property)
{
Expression result = null;
foreach (string propertyName in property.Split('.'))
{
Expression instance = result ?? initialInstance;
result = Expression.Property(instance, propertyName);
}
return result;
}

Categories