Dynamically construct an expression tree - c#

I have the following setup:
public class ProductSpecification {
public string Name { get; set; }
public string Value { get; set; }
}
public class Product {
public IEnumerable<ProductSpecification> Specifications { get; set; }
}
I want to construct the following ExpressionTree in C# programmatically:
p => p.Specifications.Any(s=>s.Name.Contains("name_val") && s.Value.Contains("value_val"))
I've managed to do this so far, but not even close to the end result:
public ExpressionBuilder AndStartsWith(string property, string value)
{
var arguments = new Dictionary<object, Type>
{
{ value, typeof(string)},
{ StringComparison.InvariantCultureIgnoreCase, typeof(StringComparison)}
};
MethodCallExpression methodExpression = GetExpressionMethod(property, "StartsWith", arguments);
_accumulator = Expression.AndAlso(_accumulator, methodExpression);
return this;
}
private MethodCallExpression GetExpressionMethod(string property, string methodName, Dictionary<object, Type> arguments) {
MethodInfo method = typeof(string).GetMethod(methodName, arguments.Values.ToArray());
Expression source = GetExpressionBody(property);
IEnumerable<ConstantExpression> argumentsExpressions = arguments.Select(x => Expression.Constant(x.Key, x.Value));
MethodCallExpression methodExpression = Expression.Call(source, method, argumentsExpressions);
return methodExpression;
}
Could you hint me to a solution?

Hint, you can view the target expression tree you're trying to build by letting the compiler build it for you.
Expression<Func<Product, bool>> expr = p =>
p.Specifications.Any(s=>s.Name.Contains("name_val") && s.Value.Contains("value_val"));
Anyway, here's how you can build that expression:
public Expression<Func<Product, bool>> GenerateProductExpression()
{
var param = Expression.Parameter(typeof(Product), "p");
var nameValue = Expression.Constant("name_val");
var valueValue = Expression.Constant("name_val");
var specifications = Expression.Property(param, "Specifications");
var body =
Expression.Call(typeof(Enumerable), "Any", new[] { typeof(ProductSpecification) },
specifications, GenerateAnyPredicate(nameValue, valueValue)
);
return Expression.Lambda<Func<Product, bool>>(body, param);
}
public Expression<Func<ProductSpecification, bool>> GenerateAnyPredicate(
Expression nameValue, Expression valueValue)
{
var param = Expression.Parameter(typeof(ProductSpecification), "s");
var name = Expression.Property(param, "Name");
var value = Expression.Property(param, "Value");
var body = Expression.AndAlso(
Expression.Call(name, "Contains", null, nameValue),
Expression.Call(value, "Contains", null, valueValue)
);
return Expression.Lambda<Func<ProductSpecification, bool>>(body, param);
}

Related

Create a method which create dynamic expression

I want to create a method which return dictionary like below. But, I want being generic method which paremerters ara EntityType and columnNameList. I want to call like this,
My method calling:
CreateColumnMap<Student>(new List<string>{"Name","Surname","Age"});
My return value
var columnsMap = new Dictionary<string, Expression<Func<Student, object>>>()
{
["Name"] = v => v.Name,
["Surname"] = v => v.Surname,
["Age"] = v => v.Age
};
Student.cs
public class Student
{
public string Name { get; set; }
public string Surname { get; set; }
public string Age { get; set; }
public string SchoolName { get; set; }
}
I started function like below. But i can't complete. How can i complete "???" part.
public Dictionary<string, Expression<Func<T, object>>> CreateColumnMap<T>(List<string> columNameList)
{
var dictionary = new Dictionary<string, Expression<Func<T, object>>>();
foreach (var columnName in columNameList)
{
//??????
dictionary.Add(); //????????????????????
//??????
}
return dictionary;
}
Reference Creating Expression Trees by Using the API
Use the Expression class and its static factory methods to manually build the desired expression based on the provided member name from the generic argument type.
For example, the following uses the Parameter and Property factory methods to manually build the expression tree nodes for the lambda expression v => v.PropertyName
Expression<Func<TModel, object>> GetPropertyExpression<TModel>(string propertyName) {
// Manually build the expression tree for
// the lambda expression v => v.PropertyName.
// (TModel v) =>
var parameter = Expression.Parameter(typeof(TModel), "v");
// (TModel v) => v.PropertyName
var property = Expression.Property(parameter, propertyName);
var expression = Expression.Lambda<Func<TModel, object>>(property, parameter);
return expression;
}
You can then apply the above
public Dictionary<string, Expression<Func<T, object>>> CreateColumnMap<T>(List<string> columNameList) {
var dictionary = new Dictionary<string, Expression<Func<T, object>>>();
foreach (var columnName in columNameList) {
dictionary[columnName] = GetPropertyExpression<T>(columnName);
}
return dictionary;
}

How to add additional lambda expression to IQueryable CreateQuery

Here is my original select query which works for one field "SurveyName":
public static IQueryable Select(this IQueryable source, string selector, params object[] values)
{
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, "SurveyName", values); //SurveyName is hard coded here as example
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Select",
new Type[] { source.ElementType, lambda.Body.Type },
source.Expression, Expression.Quote(lambda)));
}
I want to add second lambda expression and I tried below:
public static IQueryable Select(this IQueryable source, string selector, params object[] values)
{
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, "SurveyName", values); //SurveyName is hard coded as example
LambdaExpression lambda2 = DynamicExpression.ParseLambda(source.ElementType, null, "SurveyHeaderName", values); //SurveyHeaderName is hard coded as example
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Select",
new Type[] { source.ElementType, lambda.Body.Type, lambda2.Body.Type },
source.Expression, Expression.Quote(lambda)));
}
Which give me
No generic method 'Select' on type 'System.Linq.Queryable' is
compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic
How to achieve add additional lambda expression (I mean additional field Select SurveyName, SurveyHeaderName)
Hear is "lambda" screen shot in debug
As giving more details, why I use above code, here is frontend part:
public class SurveyQuestionSurveyHeaderSurvey
{
public SurveyQuestion SurveyQuestion { get; set; }
public SurveyHeader SurveyHeader { get; set; }
public Survey Survey { get; set; }
public String SurveyName { get; set; }
public String SurveyHeaderName { get; set; }
}
public IEnumerable<SurveyQuestionReportViewModel> SurveyQuestionReportExcel(IDataTablesRequest dataTablesRequest, out int totalCount)
{
var res0 = from sq in _uow.Repository<SurveyQuestion>().Queryable()
join s in _uow.Repository<Survey>().Queryable()
on sq.SurveyHeader.SurveyId equals s.SurveyId
select new SurveyQuestionSurveyHeaderSurvey { SurveyQuestion = sq, Survey = s, SurveyHeader = sq.SurveyHeader, SurveyName = s.Name };
List<Expression<Func<object, bool>>> _expression;
List<Expression<Func<object, object>>> _includes;
_includes = new List<Expression<Func<object, object>>>();
_expression = new List<Expression<Func<object, bool>>>();
Func<IQueryable<object>, IOrderedQueryable<object>> _orderBy;
if (dataTablesRequest.GetSearchListParameter() != null)
_expression.Add(DynamicQueryable.WhereLambdaExpression<object>(dataTablesRequest.GetSearchListParameter()));
if (dataTablesRequest.GetOrderByParameter() != null)
_orderBy = p => p.OrderBy(dataTablesRequest.GetOrderByParameter());
else
_orderBy = p => p.OrderBy("ID Desc");
var fields = String.Join(",", dataTablesRequest.Columns.Select(x => x.Data).ToArray()).TrimEnd(',');
fields = "SurveyName";
var toto = res0.Select(fields, null, _expression, _orderBy, _includes, dataTablesRequest.Start, dataTablesRequest.Length);
var result = res0.Include(p => p.SurveyHeader.Survey).Where(x => x.SurveyHeader.SurveyId == 3074)
.Select(x => new SurveyQuestionReportViewModel
{
SurveyQuestionId = x.SurveyQuestion.SurveyQuestionId,
SurveyName = x.SurveyHeader.Survey.Name,
Name = "Genel Sonuç",
HeaderName = x.SurveyHeader.Name,
QuestionName = x.SurveyQuestion.Description,
});
totalCount = 10;
return result;
}
I first build my lambda expression by:
static Expression<Func<T, T>> BuildLambda<T>()
{
var createdType = typeof(T);
var displayValueParam = Expression.Parameter(typeof(T), "Param_0");
var ctor = Expression.New(createdType);
var bindings = new List<MemberAssignment>();
foreach (var propertyInfo in typeof(T).GetProperties())
{
Expression left = Expression.Property(displayValueParam, propertyInfo);
var displayValueProperty = propertyInfo;
bindings.Add(Expression.Bind(displayValueProperty, left));
}
var memberInit = Expression.MemberInit(ctor, bindings);
return
Expression.Lambda<Func<T, T>>(memberInit, displayValueParam);
}
And my select is :
public static IQueryable<T> Select<T>(this IQueryable source, IDataTablesRequest dataTablesRequest, out int totalCount)
{
var lambda = BuildLambda<T>();
var query = source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Select",
new Type[] { source.ElementType, lambda.Body.Type },
source.Expression
, Expression.Quote((Expression)lambda)
));
if (dataTablesRequest.GetSearchListParameter() != null)
query = query.Where(dataTablesRequest.GetSearchListParameter());
totalCount = query.Count();
if (dataTablesRequest.GetOrderByParameter() != null)
query = query.OrderBy(dataTablesRequest.GetOrderByParameter(), null);
query = query.Skip(dataTablesRequest.Start).Take(dataTablesRequest.Length);
return (IQueryable<T>)query;
}

dynamically build select clause linq

class someClass
{
public int Id { get; set; }
public string Name{ get; set; }
...
public string someProperty { get; set; }
}
Expression<Func<someClass, object>> selector = null;
selector = k => new { k.Id ,k.Name };
var serult = myData.Select(selector);
// .Select(p=> new {p.Name , p.Id}) etc.
This sample code is working
But;
Expression<Func<someClass, ???>> createSelector(string[] fields)
{
...
....
return ...
}
Expression<Func<someClass, ???>> selector = createSelector({"Name","Id"});
Is this possible?
This method when running create dynamic selector
This can be used to create the expression you need.
public static Expression<Func<T, TReturn>> CreateSelector<T, TReturn>(string fieldName)
where T : class
where TReturn : class
{
ParameterExpression p = Expression.Parameter(typeof(T), "t");
Expression body = Expression.Property(p, fieldName);
Expression conversion = Expression.Convert(body, typeof(object));
return Expression.Lambda<Func<T, TReturn>>(conversion, new ParameterExpression[] { p });
}

The parameter 'd' was not bound in the specified LINQ to Entities query expression

What is this error :
The parameter 'd' was not bound in the specified LINQ to Entities
query expression
See details :
private static IEnumerable<T> ConnectToDatabase(IQueryable dbSet, ParameterExpression pe, IEnumerable<Expression> expressions,
string orderby, bool desc)
{
// expressions =
Expression body = null;
if (expressions.Any())
{
foreach (Expression expression in expressions)
{
body = ExpressionExtensions.JoinExpressions(body == null, Expression.Or, body, expression);
}
}
if (body == null)
{
Expression left = Expression.Property(pe, "ID");
Expression right = Expression.Constant(-1);
body = Expression.NotEqual(left, right);
}
IQueryable<T> results;
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { dbSet.ElementType },
dbSet.Expression,
Expression.Lambda<Func<T, bool>>(body, new[] { pe }));
var ModelType = typeof(T);
pe = Expression.Parameter(ModelType, "x");
var propertyInfoOrderBy = GetPropertyInfo(orderby);
var propertyAccess = Expression.MakeMemberAccess(pe, propertyInfoOrderBy);
var orderByExp = Expression.Lambda(propertyAccess, pe);
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] { ModelType, propertyInfoOrderBy.PropertyType },
whereCallExpression,
Expression.Quote(orderByExp));
if (desc)
{
MethodCallExpression resultExp = Expression.Call(
typeof(Queryable),
"OrderByDescending",
new[] { ModelType, propertyInfoOrderBy.PropertyType },
orderByCallExpression,
Expression.Quote(orderByExp));
results = dbSet.Provider.CreateQuery<T>(resultExp);
}
else
{
results = dbSet.Provider.CreateQuery<T>(orderByCallExpression);
}
return results.ToList();
}
expressions :
body :
whereCallExpression :
orderByCallExpression :
error :
JN_News class :
public class JN_News
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string NewsLink { get; set; }
public DateTime PubDate { get; set; }
public string ImageLink { get; set; }
public bool IsDisplay { get; set; }
public Decimal? Rate { get; set; }
public int NewsCategories_ID { get; set; }
public virtual JN_NewsCategories JN_NewsCategories { get; set; }
}
JN_NewsCategories class :
public class JN_NewsCategories
{
public int ID { get; set; }
public string NewsCategoriesFa { get; set; }
public string NewsCategoriesEn { get; set; }
public bool IsGetNews { get; set; }
public virtual ICollection<JN_News> JN_News { get; set; }
public JN_NewsCategories()
{
JN_News = new Collection<JN_News>();
}
}
update :
When I've eliminated the following two statements. Working properly:
my all code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace NewsSiteApk.Data.DynamicSearchLibrary
{
public static class SearchUsingExpression<T> where T : class
{
public static IEnumerable<T> Search(IEnumerable<T> listOfT, string search, string orderBy, bool desc, int pageIndex, int pageSize)
{
listOfT = GetListOfData(listOfT, search, orderBy, desc).Skip((pageIndex - 1) * pageSize).Take(pageSize);
return listOfT;
}
public static int GetCount(IEnumerable<T> listOfT, string search)
{
listOfT = GetListOfData(listOfT, search, "id", true);
return listOfT.Count();
}
private static IEnumerable<T> GetListOfData(IEnumerable<T> listOfT, string search, string orderBy, bool desc)
{
var modelType = typeof(T);
ParameterExpression pe = Expression.Parameter(modelType, "d");
var expressions = new List<Expression>();
if (!string.IsNullOrEmpty(search))
{
expressions.AddRange(GetExpressions(modelType.Name, search, pe));
}
var connectToDatabase = ConnectToDatabase(listOfT.AsQueryable(), pe, expressions, orderBy, desc);
return connectToDatabase;
}
private static IEnumerable<T> ConnectToDatabase(IQueryable dbSet, ParameterExpression pe, IEnumerable<Expression> expressions,
string orderby, bool desc)
{
Expression body = null;
if (expressions.Any())
{
foreach (Expression expression in expressions)
{
body = ExpressionExtensions.JoinExpressions(body == null, Expression.Or, body, expression);
}
}
if (body == null)
{
Expression left = Expression.Property(pe, "ID");
Expression right = Expression.Constant(-1);
body = Expression.NotEqual(left, right);
}
IQueryable<T> results;
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { dbSet.ElementType },
dbSet.Expression,
Expression.Lambda<Func<T, bool>>(body, new[] { pe }));
var propertyInfoOrderBy = GetPropertyInfo(orderby);
var propertyAccess = Expression.MakeMemberAccess(pe, propertyInfoOrderBy);
var orderByExp = Expression.Lambda(propertyAccess, pe);
var ModelType = typeof(T);
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] { ModelType, propertyInfoOrderBy.PropertyType },
whereCallExpression,
Expression.Quote(orderByExp));
if (desc)
{
MethodCallExpression resultExp = Expression.Call(
typeof(Queryable),
"OrderByDescending",
new[] { ModelType, propertyInfoOrderBy.PropertyType },
orderByCallExpression,
Expression.Quote(orderByExp));
results = dbSet.Provider.CreateQuery<T>(resultExp);
}
else
{
results = dbSet.Provider.CreateQuery<T>(orderByCallExpression);
}
return results.ToList();
}
private static IEnumerable<Expression> GetExpressions(string modelName, string search, ParameterExpression pe)
{
var expressions = new List<Expression>();
var fieldModels = GetFields(modelName);
foreach (FieldModel fieldModel in fieldModels)
{
IEnumerable<Expression> conditionsWithSubModel;
if (fieldModel.NameEn.Contains("[]"))
{
conditionsWithSubModel = GetConditionsWithSubModelList(search, fieldModel.NameEn, pe);
}
else if (fieldModel.NameEn.Contains("."))
{
conditionsWithSubModel = GetConditionsWithSubModel(search, fieldModel.NameEn);
}
else
{
conditionsWithSubModel = GetConditionsWithoutSubModel(search, pe, fieldModel.NameEn);
}
if (conditionsWithSubModel != null)
expressions.AddRange(conditionsWithSubModel);
}
return expressions;
}
private static IEnumerable<Expression> GetConditionsWithoutSubModel(string search, ParameterExpression pe,
string parametr)
{
var expressions = new List<Expression>();
foreach (var splitSeacrh in search.Split(' '))
{
Expression left = Expression.Property(pe, parametr);
Expression right = Expression.Constant(splitSeacrh);
MethodCallExpression conditionExpression = Expression.Call(left, typeof(string).GetMethod("Contains"),
right);
expressions.Add(conditionExpression);
}
return expressions;
}
private static IEnumerable<Expression> GetConditionsWithSubModel(string search,
string parameter)
{
// output.Where(d => d.JN_NewsCategories.NewsCategoriesEn.Contains(""));
var expressions = new List<Expression>();
var strings = parameter.Split('$');
var modelName = strings[0]; // Like : JN_News
var subModelName = strings[1].Split('.')[0];// Like : JN_NewsCategories
var subModelField = strings[1].Split('.')[1];// Like : NewsCategoriesEn
foreach (var splitSeacrh in search.Split(' '))
{
Type modelClass = GetModel(modelName);
Type submodelClass = GetModel(subModelName);
ParameterExpression peSubModel = Expression.Parameter(modelClass, "d");
Expression leftSubModel = Expression.Property(peSubModel, modelClass.GetProperty(subModelName));
Expression ex = Expression.Property(leftSubModel, submodelClass.GetProperty(subModelField));
Expression rightSubModel = Expression.Constant(splitSeacrh);
MethodCallExpression conditionExpressionSubModel = Expression.Call(ex,
typeof(string).GetMethod("Contains"), rightSubModel);
expressions.Add(conditionExpressionSubModel);
}
return expressions;
}
private static IEnumerable<Expression> GetConditionsWithSubModelList(string search, string parameter,
ParameterExpression peModel)
{
parameter = parameter.Replace("[]", string.Empty);
var expressions = new List<Expression>();
var subModelName = parameter.Split('.')[0];
var subModelField = parameter.Split('.')[1];
foreach (var splitSeacrh in search.Split(' '))
{
Type modelClass = GetModel(subModelName);
var subModelProperty = GetPropertyInfo(subModelName);
ParameterExpression peSubModel = Expression.Parameter(modelClass, "d");
Expression leftSubModel = Expression.Property(peSubModel, modelClass.GetProperty(subModelField));
Expression rightSubModel = Expression.Constant(splitSeacrh);
MethodCallExpression conditionExpressionSubModel = Expression.Call(leftSubModel,
typeof(string).GetMethod("Contains"), rightSubModel);
LambdaExpression anyLambdaSubModelForModel = Expression.Lambda(conditionExpressionSubModel, peSubModel);
MethodInfo anyMethodForModel = CreateAnyMethodGeneric(subModelProperty);
Expression lambedaSubModelForExpressionModel = Expression.Property(peModel, subModelProperty);
Expression expression = Expression.Call(anyMethodForModel, lambedaSubModelForExpressionModel,
anyLambdaSubModelForModel);
expressions.Add(expression);
}
return expressions;
}
private static Type GetModel(string name)
{
return (typeof(T).Assembly).GetTypes()
.First(d => string.Equals(d.Name, name, StringComparison.CurrentCultureIgnoreCase));
}
private static PropertyInfo GetPropertyInfo(string name)
{
return typeof(T).GetProperties().First(d => string.Equals(d.Name, name, StringComparison.CurrentCultureIgnoreCase));
}
private static MethodInfo CreateAnyMethodGeneric(PropertyInfo propYekiBeAkhar, string methodName = "Any")
{
return
typeof(Enumerable).GetMethods()
.Single(m => m.Name == methodName && m.GetParameters().Length == 2)
.MakeGenericMethod(propYekiBeAkhar.PropertyType.GenericTypeArguments[0]);
}
private static IEnumerable<FieldModel> GetFields(string modelName)
{
var fieldsFactory = new FieldsFactory();
var fieldModels = fieldsFactory.GetClause(modelName);
return fieldModels;
}
}
}
field factory class :
public class FieldsFactory
{
public List<FieldModel> GetClause(string modelName)
{
var type = typeof(FieldsFactory);
var methodInfos = type.GetMethod("Get" + modelName + "Fields", BindingFlags.NonPublic | BindingFlags.Instance);
var listOfFields = (List<FieldModel>)methodInfos.Invoke(this, null);
return listOfFields;
}
private List<FieldModel> GetJN_NewsCategoriesFields()
{
var fields = new List<FieldModel>
{
new FieldModel
{
NameEn = "NewsCategoriesFa",
},
new FieldModel
{
NameEn = "NewsCategoriesEn",
},
new FieldModel
{
NameEn = "JN_News[].Title",
},
new FieldModel
{
NameEn = "JN_News[].Description",
}
};
return fields;
}
private List<FieldModel> GetJN_NewsFields()
{
var fields = new List<FieldModel>
{
new FieldModel
{
NameEn = "Title",
},
new FieldModel
{
NameEn = "JN_News$JN_NewsCategories.NewsCategoriesFa",
},
new FieldModel
{
NameEn = "JN_News$JN_NewsCategories.NewsCategoriesEn",
}
};
return fields;
}
}
i fooooooooooound. I'm very happy.
Only , I replaced the following method :
private static IEnumerable<Expression> GetConditionsWithSubModel(string search, ParameterExpression pe,
string parameter)
{
// output.Where(d => d.JN_NewsCategories.NewsCategoriesEn.Contains(""));
var expressions = new List<Expression>();
var strings = parameter.Split('$');
var modelName = strings[0];
var subModelName = strings[1].Split('.')[0];
var subModelField = strings[1].Split('.')[1];
foreach (var splitSeacrh in search.Split(' '))
{
Type modelClass = GetModel(modelName);
Type submodelClass = GetModel(subModelName);
ParameterExpression peSubModel = Expression.Parameter(modelClass, "d");
Expression leftSubModel = Expression.Property(peSubModel, modelClass.GetProperty(subModelName));
Expression ex = Expression.Property(leftSubModel, submodelClass.GetProperty(subModelField));
Expression rightSubModel = Expression.Constant(splitSeacrh);
MethodCallExpression conditionExpressionSubModel = Expression.Call(ex,
typeof(string).GetMethod("Contains"), rightSubModel);
expressions.Add(conditionExpressionSubModel);
}
return expressions;
}
with following method :
private static IEnumerable<Expression> GetConditionsWithSubModel(string search, ParameterExpression pe,
string parameter)
{
// output.Where(d => d.JN_NewsCategories.NewsCategoriesEn.Contains(""));
var expressions = new List<Expression>();
var strings = parameter.Split('$');
var modelName = strings[0];
var subModelName = strings[1].Split('.')[0];
var subModelField = strings[1].Split('.')[1];
foreach (var splitSeacrh in search.Split(' '))
{
Type modelClass = GetModel(modelName);
Type submodelClass = GetModel(subModelName);
Expression leftSubModel = Expression.Property(pe, modelClass.GetProperty(subModelName));
Expression ex = Expression.Property(leftSubModel, submodelClass.GetProperty(subModelField));
Expression rightSubModel = Expression.Constant(splitSeacrh);
MethodCallExpression conditionExpressionSubModel = Expression.Call(ex,
typeof(string).GetMethod("Contains"), rightSubModel);
expressions.Add(conditionExpressionSubModel);
}
return expressions;
}

Lambda expression to access a property of an object that is property of another object in c#

I have this two classes:
public class Contratos
{
//...
public int EntidadeFinanceiraId { get; set; }
[Column("Nome")]
public EntidadesFinanceiras entidadeFinanceira { get; set; }
//...
}
public class EntidadesFinanceiras
{
[Key]
public int ID { get; set; }
public string Nome { get; set; }
//...
}
and want to dinamically filter a List of Contratos based on Contratos.entidadeFinanceira.Nome. This is part of a Method that filters the list based on a property selected by the user.
public IQueryable<Models.Contratos> applyLambdaFilter(string val, string col, string oper, IQueryable<Models.Contratos> contratosList)
{
if (!string.IsNullOrWhiteSpace(val))
{
string typeName;
string columnName;
Type propType;
string[] propName = col.Split(new[] { '.' });
if (propName.Count() > 1)
{
typeName = "GAcordos.Models." + propName[0]; //entidadeFinanceira
columnName = propName[1]; //Nome
propType = Type.GetType("GAcordos.Models.Contratos").GetProperty(propName[0]).PropertyType.GetProperty(columnName).PropertyType; //String
}
else
{
typeName = "GAcordos.Models.Contratos";
columnName = propName[0]; //Other Contratos property
propType = Type.GetType(typeName).GetProperty(columnName).PropertyType;
}
if (propType != null)
{
var fixedItem = Comparators.getFixedItemWithType(val, propType);
var param = Expression.Parameter(typeof(GAcordos.Models.Contratos), "x");
var body = Expression.Equal(Expression.PropertyOrField(param, col.ToString()), fixedItem, false, Type.GetType("GAcordos.Helpers.Comparators").GetMethod(oper, new Type[] { propType, propType }));
var lambda = Expression.Lambda<Func<GAcordos.Models.Contratos, bool>>(body, param);
contratosList = contratosList.Where(lambda.Compile()).AsQueryable();
}
}
return contratosList;
}
When the Method executes it throws an exception 'entidadeFinanceira.Nome' is not a member of type 'GAcordos.Models.Contratos' on the line
var body = Expression.Equal(Expression.PropertyOrField(param, col.ToString()), fixedItem, false, Type.GetType("GAcordos.Helpers.Comparators").GetMethod(oper, new Type[] { propType, propType }));
But if I write the expression directly:
contratosList = contratosList.Where(x => x.entidadeFinanceira.Nome == val);
it works fine.
So, how can I build the lambda expression x => x.property.property == constVal?
Simply, you need two uses of PropertyOrField.
Constructed manually, x => x.Foo.Bar == constVal is:
var param = Expression.Parameter(typeof(ObjectType), "x");
var lambda = Expression.Lambda<Func<ObjectType, bool>>(
Expression.Equal(
Expression.PropertyOrField(
Expression.PropertyOrField(param, "Foo"), "Bar"
), Expression.Constant(constVal, constValType)
), param);
(note that it is important to include constValType in case constVal is null; this avoids a lot of unexpected problems)
Seems that when calling
Expression.PropertyOrField(param, col.ToString())
the variable col contains "entidadeFinanceira.Nome". You may reuse all the splitting of col you did above and do something like:
Expression property = param;
foreach(var pName in propName) {
property = Expression.PropertyOrField(property, pName);
}
Now the expression property should be correct and you can use it to build the body expression:
var body = Expression.Equal(
property,
fixedItem,
false,
Type
.GetType("GAcordos.Helpers.Comparators")
.GetMethod(oper, new Type[] { propType, propType })
);

Categories