Dynamically Modifying a LINQ Query Based on a Related Table - c#

I am dynamically creating a LINQ query based on various search criteria.
As an example, let's say I am searching a Automobiles table, and I have an option to filter by ratings. I have two controls:
Compare type: [At least], [At most], [Less than], [Greater than], and [Equal].
Value: The value to compare the rating against.
So the user could, for example, select the compare type [At least] and the value 3, and my code needs to create a query that limits results to automobile ratings greater than or equal to 3.
I found a great solution given by VinayC in the question How to implement search functionality in C#/ASP.NET MVC. His DynamicWhere() method dynamically creates part of the expression that would produce the correct filter.
My problem is that my primary query type is Automobile but my ratings are in a separate table (Automobile.Ratings). How could I implement this same technique and filter on a type other than my primary query type?
Thanks for any tips.

Since the number of operations that you have is small, finite, and known at comiple time, you can simply handle it with a switch:
IQueryable<Something> query = GetQuery();
int ratingToComareWith = 1;
string operation = "Equal";
switch (operation)
{
case ("Equal"):
query = query.Where(item => item == ratingToComareWith);
break;
case ("Less Than"):
query = query.Where(item => item < ratingToComareWith);
break;
}

Here's an Entity Framework friendly alternative to expression building. Of course you will want to validate column and op to prevent SQL injection.
// User provided values
int value = 3;
string column = "Rating";
string op = "<";
// Dynamically built query
db.Database.SqlQuery<Automobile>(#"select distinct automobile.* from automobile
inner join ratings on ....
where [" + column + "] " + op + " #p0", value);

Here is a method to build conditions for nested collections or types for linq-to-entities.
Restructured for your needs:
public static Expression GetCondition(Expression parameter, object value, OperatorComparer operatorComparer, params string[] properties)
{
Expression resultExpression = null;
Expression childParameter, navigationPropertyPredicate;
Type childType = null;
if (properties.Count() > 1)
{
//build path
parameter = Expression.Property(parameter, properties[0]);
var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
//if it´s a collection we later need to use the predicate in the methodexpressioncall
if (isCollection)
{
childType = parameter.Type.GetGenericArguments()[0];
childParameter = Expression.Parameter(childType, childType.Name);
}
else
{
childParameter = parameter;
}
//skip current property and get navigation property expression recursivly
var innerProperties = properties.Skip(1).ToArray();
navigationPropertyPredicate = GetCondition(childParameter, test, innerProperties);
if (isCollection)
{
//build methodexpressioncall
var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2);
anyMethod = anyMethod.MakeGenericMethod(childType);
navigationPropertyPredicate = Expression.Call(anyMethod, parameter, navigationPropertyPredicate);
resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
}
else
{
resultExpression = navigationPropertyPredicate;
}
}
else
{
var childProperty = parameter.Type.GetProperty(properties[0]);
var left = Expression.Property(parameter, childProperty);
var right = Expression.Constant(value,value.GetType());
if(!new List<OperatorComparer> {OperatorComparer.Contains,OperatorComparer.StartsWith}.Contains(operatorComparer))
{
navigationPropertyPredicate = Expression.MakeBinary((ExpressionType)operatorComparer,left, right);
}
else
{
var method = GetMethod(childProperty.PropertyType, operatorComparer); //get property by enum-name from type
navigationPropertyPredicate = Expression.Call(left, method, right);
}
resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
}
return resultExpression;
}
private static MethodInfo GetMethod(Type type,OperatorComparer operatorComparer)
{
var method = type.GetMethod(Enum.GetName(typeof(OperatorComparer),operatorComparer));
return method;
}
public enum OperatorComparer
{
Equals = ExpressionType.Equal,
Contains,
StartsWith,
GreaterThan = ExpressionType.GreaterThan
....
}
private static Expression MakeLambda(Expression parameter, Expression predicate)
{
var resultParameterVisitor = new ParameterVisitor();
resultParameterVisitor.Visit(parameter);
var resultParameter = resultParameterVisitor.Parameter;
return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
}
private class ParameterVisitor : ExpressionVisitor
{
public Expression Parameter
{
get;
private set;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Parameter = node;
return node;
}
}
}
You could replace the params string[] with params Expression(Func(T,object)), if you want. Would need some more work to do it that way. You would need to definie nested collections with a syntax like
item => item.nestedCollection.Select(nested => nested.Property)
and rewrite the expression with the help of an expressionvisitor.

Related

LINQ Expression Tree - The parameter 'x' was not bound in the specified LINQ to Entities query expression

I am trying to build an expression tree dynamically to fetch data from a database.
The following codes are used for this.
Expression<Func<Client, bool>> expression = x => true;
foreach (var item in searchParams)
{
var operatorType = ExpressionType.Equal;
string propertyName = null;
object value = null;
string keyValue = item.Value;
if (item.Key == Constants.SearchParameterNames.Id)
{
int val = 0;
if (int.TryParse(keyValue, out val))
value = val;
propertyName = "ClientID";
}
else if (item.Key == Constants.SearchParameterNames.Lastupdate)
{
DateTime dateTime;
if (DateTime.TryParse(keyValue, out dateTime))
value = dateTime;
propertyName = "LastChange";
}
if (!string.IsNullOrWhiteSpace(propertyName) && value != null)
{
var exp = GetBinaryOperation<Client>(propertyName, operatorType, value);
var exp1 = Expression.And(expression.Body, exp);
expression = Expression.Lambda<Func<Client, bool>>(exp1, expression.Parameters);
}
}
var client = _clientRepository.FindBy(expression).ToList();
when _clientRepository.FindBy(expression).ToList() is executed I am getting an exception of
The parameter 'x' was not bound in the specified LINQ to Entities
query expression.
The method used to create expression:
public BinaryExpression GetBinaryOperation<T>(string propertyName, ExpressionType type, object value)
{
var parameterExpression = Expression.Parameter(typeof(T), "x");
var memberExpression = Expression.Property(parameterExpression, propertyName);
var propertyType = GetMemberType(memberExpression);
var rhs = Expression.Constant(value);
var binaryExpression = Expression.MakeBinary(type, memberExpression, rhs);
return binaryExpression;
}
When building such an expression you have to preserve the top-level parameter expression instance. When you create a new parameter expression in the GetBinaryOperation function, that will be a different instance (hence the not bound term), regardless of the fact that its name is the same "x".
Instead of creating a new parameter instance, you should pass the original LambdaExpression's "x" parameter to the GetBinaryOperation function using for example expression.Parameters[0].
All in all, you have to use the same parameter expression instance throughout the entire expression tree in this case.

Evaluating complex expression tree

I have a basic rule engine that I've built in a very similar way to the route suggested here:
How to implement a rule engine?
Ive extended it based on further requirements, and now I need to evaluate complex classes eg
EvaluateRule("Transaction.IsOpen", "Equals", "true")
The code in its most basic form is:
var param = inputMessageType;
left = Expression.Property(param, memberName);
tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
return Expression.MakeBinary(tBinary, left, right);
In order to evaluate complex classes I used a method similar to here:
https://stackoverflow.com/questions/16544672/dynamically-evaluating-a-property-string-with-expressions
The problem that Im having is that when I try to evaluate the rule with a property of a class (Transaction.IsOpen), I get it with the type of the root type on the right hand side of the expression but the type of the complex object on the left hand side of the expression.
This results in the error:
System.InvalidOperationException: The binary operator Equal is not defined for the types 'System.Func`2[Transaction,System.Boolean]' and 'System.Boolean'.
How do I overcome this problem? I am no expert using Expression Trees, and many of the concepts are proving difficult to grasp when the example strays from the standard documentation.
Edit: Here is the code(Ive omitted some stuff that is environment specific so as keep focus with the problem)
public Actions EvaluateRulesFromMessage(ClientEventQueueMessage message)
{
var ruleGroups = _ruleRepository.GetRuleList();
var actions = new Actions();
foreach (var ruleGroup in ruleGroups)
{
if (message.MessageType == "UI_UPDATE")
{
// clean up json object
JObject dsPayload = (JObject.Parse(message.Payload));
var msgParams = JsonConvert.DeserializeObject<UiTransactionUpdate>(message.Payload);
msgParams.RulesCompleted = msgParams.RulesCompleted ?? new List<int>();
var conditionsMet = false;
// process the rules filtering out the rules that have already been evaluated
var filteredRules = ruleGroup.Rules.Where(item =>
!msgParams.RulesCompleted.Any(r => r.Equals(item.Id)));
foreach (var rule in filteredRules)
{
Func<UiTransactionUpdate, bool> compiledRule = CompileRule<UiTransactionUpdate>(rule, msgParams);
if (compiledRule(msgParams))
{
conditionsMet = true;
}
else
{
conditionsMet = false;
break;
}
}
if (conditionsMet)
{
actions = AddAction(message, ruleGroup);
break;
}
}
}
return actions;
}
public Func<UiTransactionUpdate, bool> CompileRule<T>(Rule r, UiTransactionUpdate msg)
{
var expression = Expression.Parameter(typeof(UiTransactionUpdate));
Expression expr = BuildExpr<UiTransactionUpdate>(r, expression, msg);
// build a lambda function UiTransactionUpdate->bool and compile it
return Expression.Lambda<Func<UiTransactionUpdate, bool>>(expr, expression).Compile();
}
static Expression Eval(object root, string propertyString, out Type tProp)
{
Type type = null;
var propertyNames = propertyString.Split('.');
ParameterExpression param = Expression.Parameter(root.GetType());
Expression property = param;
string propName = "";
foreach (var prop in propertyNames)
{
property = MemberExpression.PropertyOrField(property, prop);
type = property.Type;
propName = prop;
}
tProp = Type.GetType(type.UnderlyingSystemType.AssemblyQualifiedName);
var param2 = MemberExpression.Parameter(tProp);
var e = Expression.Lambda(property, param);
return e;
}
static Expression BuildExpr<T>(Rule r, ParameterExpression param, UiTransactionUpdate msg)
{
Expression left;
Type tProp;
string memberName = r.MemberName;
if (memberName.Contains("."))
{
left = Eval(msg, memberName, out tProp);
}
else
{
left = Expression.Property(param, memberName);
tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
}
ExpressionType tBinary;
if (ExpressionType.TryParse(r.Operator, out tBinary))
{
Expression right=null;
switch (r.ValueType) ///todo: this needs to be refactored to be type independent
{
case TargetValueType.Value:
right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
break;
}
// use a binary operation ie true/false
return Expression.MakeBinary(tBinary, left, right);
}
else
{
var method = tProp.GetMethod(r.Operator);
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
// use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(left, method, right);
}
}
The sample code does not cover all data types used in your scenario, so it's hard to tell where it breaks exactly, but from the exception System.Func'2[Transaction,System.Boolean]' and 'System.Boolean it's obvious that on left hand you have a delegate that takes in Transaction and returns bool (Func<Transaction, bool>), and on the right hand you just have bool.
It's not possible to compare Func<Transaction, bool> to bool, but it's possible to call the function and compare its result:
Func<Transaction, bool> func = ...;
bool comparand = ...;
Transaction transaction = ...;
if (func(transaction) == comparand) { ... }
What translated to expression tree:
Expression funcExpression = ... /*LambdaExpression<Func<Transaction,bool>>*/;
Expression comparandExpression = Expression.Constant(true);
Expression transactionArg = /*e.g.*/Expression.Constant(transaction);
Expression funcResultExpression = Expression.Call(funcExpression, "Invoke", null, transactionArg);
Expression equalityTestExpression = Expression.Equal(funcResultExpression, comparandExpression);

linq orderby using property name string

I have a list which I need to order eg.
var list = someelements;
I also have a parameter which says by which property and what direction I should order list data eg.
var sortby = "Name";
var sortdirection = "desc";
I was wondering if I can match string property name with the right property so I get:
var list = someelements.OrderBy(x => x.Name).ToList();
when sortby == "Name" without the need of using switch loop to check property name and assign a correct property to OrderBy.
Same I would like to achieve with select either OrderBy or OrderByDescending depending whether I get sortdirection = "asc" or sortdirection = "desc"
Is it possible and if yes, how?
You can get Dynamic Linq through Nuget. Then you can use .OrderBy(sortby + " " + sortdirection).
Here is the link: Dynamic Linq.
Here is one solution that does not use Dynamic Linq:
public class Helper
{
public static IEnumerable<T> OrderByDynamic<T>(IEnumerable<T> items, string sortby, string sort_direction)
{
var property = typeof (T).GetProperty(sortby);
var result = typeof(Helper)
.GetMethod("OrderByDynamic_Private", BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(typeof (T), property.PropertyType)
.Invoke(null, new object[]{ items, sortby, sort_direction});
return (IEnumerable<T>)result;
}
private static IEnumerable<T> OrderByDynamic_Private<T,TKey>(IEnumerable<T> items, string sortby, string sort_direction)
{
var parameter = Expression.Parameter(typeof (T), "x");
Expression<Func<T, TKey>> property_access_expression =
Expression.Lambda<Func<T, TKey>>(
Expression.Property(parameter, sortby),
parameter);
if (sort_direction == "asc")
{
return items.OrderBy(property_access_expression.Compile());
}
if (sort_direction == "desc")
{
return items.OrderByDescending(property_access_expression.Compile());
}
throw new Exception("Invalid Sort Direction");
}
}
You can use it like this:
var result = Helper.OrderByDynamic(list, "Name", "desc").ToList();

Telerik MVC Extensions Grid - How to have the grid filter apply to initial LINQ query or get passed down to db?

Currently in my MVC grids I am using normal Server Bindings, and the filters then are appended to the URL as query strings. The problem with this method is that if I am querying a grid that by default has thousands of records, but I am only displaying the first 30 records on the first page (paging filter) of my grid. Same thing would apply to a string filter for last name. I filter my 2000 records for last name smith, get 100 records, only 30 displayed on first page. I will then actually query a person object, have the full 2k objects returned, filter it to 100, and display 30. This is horribly inefficient.
How does one pass the filter parameters into a LINQ query for example so the initial query only returns results shown on that page? Also is there some automated way to do this generically for any grid? Or would you have to write this logic for every single grid you have?
I know if ToGridModel, which I use when exporting a grid to excel:
public ActionResult Export(int page, string orderBy, string filter, string groupBy)
{
//Get the data representing the current grid state - page, sort and filter
List<Building> buildings = _buildingService.GetList();
List<BuildingModel> buildingModels = new List<BuildingModel>();
buildings.ForEach(x => buildingModels.Add(_buildingService.ConvertToModel(x)));
GridModel model = buildingModels.AsQueryable().ToGridModel(1, buildingModels.Count, orderBy, groupBy, filter);
MemoryStream fileOutput = ExcelUtil.ExportGridModelToExcel<BuildingModel>(model);
return File(fileOutput.ToArray(), //The binary data of the XLS file
"application/vnd.ms-excel", //MIME type of Excel files
"BuildingsReport.xls"); //Suggested file name in the "Save as" dialog which will be displayed to the end user
}
but I guess then another problem is that the grid itself is made up of ViewModels, not the POCO object. So even then, when I export to excel. I have to requery the whole result set, and then filter it down.
Surely there is a better way to do this?
You can use Custom Binding to do this.
simple example of it you can read here: Telerik Documentation
For more generic approach you can use method CreateFilterExpression of class FilterDescriptor
Update
Generic example:
[GridAction(EnableCustomBinding = true)]
public ViewResult GetRecords(GridCommand command)
{
using (var context = _contextFactory())
{
var records = context.Set<Records>();
if (command.FilterDescriptors.Any()) //RequestNumber
{
var filter = command.FilterDescriptors.GetFilter<ChangeRecord>();
records = records.Where(filter);
}
return View(new GridModel(records.ToArray()));
}
}
public static class GridCommandExtensions
{
public static Expression<Func<TGridModel, bool>> GetFilter<TGridModel>(this IList<IFilterDescriptor> filterDescriptors)
{
var filters = filterDescriptors.SelectMany(GetAllFilterDescriptors).ToArray();
var parameter = Expression.Parameter(typeof(TGridModel), "c");
if (filters.Length == 1)
return Expression.Lambda<Func<TGridModel, bool>>(GetExpression(parameter, filters[0]), parameter);
Expression exp = null;
for (int index = 0; index < filters.Length; index += 2) // условие И
{
var filter1 = filters[index];
if (index == filters.Length - 1)
{
exp = Expression.AndAlso(exp, GetExpression(parameter, filter1));
break;
}
var filter2 = filters[index + 1];
var left = GetExpression(parameter, filter1);
var right = GetExpression(parameter, filter2);
exp = exp == null
? Expression.AndAlso(left, right)
: Expression.AndAlso(exp, Expression.AndAlso(left, right));
}
return Expression.Lambda<Func<TGridModel, bool>>(exp, parameter);
}
private static Expression GetExpression(ParameterExpression parameter, FilterDescriptor filter)
{
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var startsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var property = filter.Member.Contains(".") ?
filter.Member.Split('.').Aggregate((Expression)parameter, Expression.Property) // (x => x.Property.FieldName)
: Expression.Property(parameter, filter.Member); // (x => x.FieldName)
var constant = Expression.Constant(filter.Value); // значение для выражения
switch (filter.Operator)
{
case FilterOperator.IsEqualTo:
return Expression.Equal(property, constant);
case FilterOperator.IsNotEqualTo:
return Expression.NotEqual(property, constant);
case FilterOperator.Contains:
return Expression.Call(property, containsMethod, constant);
case FilterOperator.StartsWith:
return Expression.Call(property, startsWithMethod, constant);
case FilterOperator.EndsWith:
return Expression.Call(property, endsWithMethod, constant);
case FilterOperator.IsGreaterThan:
return Expression.GreaterThan(property, constant);
case FilterOperator.IsGreaterThanOrEqualTo:
return Expression.GreaterThanOrEqual(property, constant);
case FilterOperator.IsLessThan:
return Expression.LessThan(property, constant);
case FilterOperator.IsLessThanOrEqualTo:
return Expression.LessThanOrEqual(property, constant);
default:
throw new InvalidOperationException(string.Format("Неподдерживаемая операция {0} для колонки {1}", filter.Operator, filter.Member));
}
}
public static IEnumerable<FilterDescriptor> GetAllFilterDescriptors(this IFilterDescriptor descriptor)
{
var filterDescriptor = descriptor as FilterDescriptor;
if (filterDescriptor != null)
{
yield return filterDescriptor;
yield break;
}
var compositeFilterDescriptor = descriptor as CompositeFilterDescriptor;
if (compositeFilterDescriptor != null)
{
if (compositeFilterDescriptor.LogicalOperator == FilterCompositionLogicalOperator.Or)
throw new ArgumentOutOfRangeException("descriptor", "В фильтрах не поддерживается OR");
foreach (var childDescriptor in compositeFilterDescriptor.FilterDescriptors.SelectMany(GetAllFilterDescriptors))
yield return childDescriptor;
}
}
}
if(request.Filters.Count > 0)
{
foreach (Kendo.Mvc.FilterDescriptor f in request.Filters)
{
f.Value = f.Value.ToString().Trim();
}
}
I prefer to use
IEnumerable<Building> buildings = _buildingService.GetIEnumerable().AsQueryable().ToGridModel(page, pageSize, orderBy, string.Empty, filter).Data.Cast<Building>();

Dynamic expression tree to filter on nested collection properties

I'm using Entity Framework and building queries using navigation properties dynamically.
For most of my use cases, the following works fine:
private static MethodCallExpression GetNavigationPropertyExpression<T>(string propertyName, int test,
ParameterExpression parameter, string subParameter)
{
var navigationPropertyCollection = Expression.Property(parameter, propertyName);
var childType = navigationPropertyCollection.Type.GetGenericArguments()[0];
var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(childType);
var aclAttribute = GetAclAttribute(typeof(T), propertyName);
var childProperty = aclAttribute.ChildProperty;
var propertyCollectionGenericArg = childType;
var serviceLocationsParam = Expression.Parameter(propertyCollectionGenericArg, subParameter);
var left = Expression.Property(serviceLocationsParam, childProperty);
var right = Expression.Constant(test, typeof(int));
var isEqual = Expression.Equal(left, right);
var subLambda = Expression.Lambda(isEqual, serviceLocationsParam);
var resultExpression = Expression.Call(anyMethod, navigationPropertyCollection, subLambda);
return resultExpression;
}
I use a custom AclAttribute class assigned to properties via metadata type and partial classes. For navigation properties, a ChildProperty is provided so that the expression builder knows to look deeper for the desired property.
For example: the table Services references another table called ServiceLocations. I need the LocationId value(s) from the ServiceLocations reference. This part works fine.
My problem is when there is more than 1 nested property to go through. Another example: the table ServiceCategories references Services which, again, references ServiceLocations. Like before, I need the LocationId value(s) from ServiceLocations.
I've done this manually by using two "Any" methods, combining, and returning the resulting expression, but there will be times where navigation properties won't be collections, or a child of a navigation property may be a collection. For those cases, I need some kind of recursive option. I've been trying for a while now, and coming up short.
Since I mentioned it, here's the test method I put together for my second example. This works, but I'm violating DRY. (Note: I didn't name the tables):
private static MethodCallExpression GetNestedNavigationPropertyExpression(int test, ParameterExpression rootParameter)
{
var servicesProperty = Expression.Property(rootParameter, "tblServices");
var servicesParameter = Expression.Parameter(servicesProperty.Type.GetGenericArguments()[0], "ss");
var serviceLocationsProperty = Expression.Property(servicesParameter, "tblServiceLocations");
var serviceLocationsParameter = Expression.Parameter(serviceLocationsProperty.Type.GetGenericArguments()[0], "s");
var servicesAnyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(servicesProperty.Type.GetGenericArguments()[0]);
var serviceLocationsAnyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(serviceLocationsProperty.Type.GetGenericArguments()[0]);
var aclAttribute = GetAclAttribute(typeof(tblService), "tblServiceLocations");
var left = Expression.Property(serviceLocationsParameter, aclAttribute.ChildProperty);
var right = Expression.Constant(test, typeof(int));
var isEqual = Expression.Equal(left, right);
var subLambda = Expression.Lambda(isEqual, serviceLocationsParameter);
var endExpression = Expression.Call(serviceLocationsAnyMethod, serviceLocationsProperty, subLambda);
var intermediaryLamba = Expression.Lambda(endExpression, servicesParameter);
var resultExpression = Expression.Call(servicesAnyMethod, servicesProperty, intermediaryLamba);
return resultExpression;
}
With this it should be possible to build your queries:
public static Expression GetNavigationPropertyExpression(Expression parameter, int test, params string[] properties)
{
Expression resultExpression = null;
Expression childParameter, navigationPropertyPredicate;
Type childType = null;
if (properties.Count() > 1)
{
//build path
parameter = Expression.Property(parameter, properties[0]);
var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
//if it´s a collection we later need to use the predicate in the methodexpressioncall
if (isCollection)
{
childType = parameter.Type.GetGenericArguments()[0];
childParameter = Expression.Parameter(childType, childType.Name);
}
else
{
childParameter = parameter;
}
//skip current property and get navigation property expression recursivly
var innerProperties = properties.Skip(1).ToArray();
navigationPropertyPredicate = GetNavigationPropertyExpression(childParameter, test, innerProperties);
if (isCollection)
{
//build methodexpressioncall
var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2);
anyMethod = anyMethod.MakeGenericMethod(childType);
navigationPropertyPredicate = Expression.Call(anyMethod, parameter, navigationPropertyPredicate);
resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
}
else
{
resultExpression = navigationPropertyPredicate;
}
}
else
{
//Formerly from ACLAttribute
var childProperty = parameter.Type.GetProperty(properties[0]);
var left = Expression.Property(parameter, childProperty);
var right = Expression.Constant(test, typeof(int));
navigationPropertyPredicate = Expression.Equal(left, right);
resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
}
return resultExpression;
}
private static Expression MakeLambda(Expression parameter, Expression predicate)
{
var resultParameterVisitor = new ParameterVisitor();
resultParameterVisitor.Visit(parameter);
var resultParameter = resultParameterVisitor.Parameter;
return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
}
private class ParameterVisitor : ExpressionVisitor
{
public Expression Parameter
{
get;
private set;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Parameter = node;
return node;
}
}
Call it like:
var parameter = Expression.Parameter(typeof(A), "A");
var expression = ExpressionBuilder.GetNavigationPropertyExpression(parameter, 8,"CollectionOfB", "CollectionOfC", "ID");

Categories