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 });
}
Related
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;
}
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);
}
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;
}
I am looking for a generic Filter for the searchText in the query of any Column/Field mapping
public static IQueryable<T> Filter<T>(this IQueryable<T> source, string searchTerm)
{
var propNames = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(e=>e.PropertyType == typeof(String)).Select(x => x.Name).ToArray();
//I am getting the property names but How can I create Expression for
source.Where(Expression)
}
Here I am giving you an example scenario
Now From my HTML5 Table in Asp.net MVC4 , I have provided a Search box to filter results of entered text , which can match any of the below columns/ Menu class Property values , and I want to do this search in Server side , how can I implement it.
EF Model Class
public partial class Menu
{
public int Id { get; set; }
public string MenuText { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
public string Icon { get; set; }
public string ToolTip { get; set; }
public int RoleId { get; set; }
public virtual Role Role { get; set; }
}
You can use Expressions:
private static Expression<Func<T, bool>> GetColumnEquality<T>(string property, string term)
{
var obj = Expression.Parameter(typeof(T), "obj");
var objProperty = Expression.PropertyOrField(obj, property);
var objEquality = Expression.Equal(objProperty, Expression.Constant(term));
var lambda = Expression.Lambda<Func<T, bool>>(objEquality, obj);
return lambda;
}
public static IQueryable<T> Filter<T>(IQueryable<T> source, string searchTerm)
{
var propNames = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(e => e.PropertyType == typeof(string))
.Select(x => x.Name).ToList();
var predicate = PredicateBuilder.False<T>();
foreach(var name in propNames)
{
predicate = predicate.Or(GetColumnEquality<T>(name, searchTerm));
}
return source.Where(predicate);
}
Combined with the name PredicateBuilder from C# in a NutShell. Which is also a part of LinqKit.
Example:
public class Foo
{
public string Bar { get; set; }
public string Qux { get; set; }
}
Filter<Foo>(Enumerable.Empty<Foo>().AsQueryable(), "Hello");
// Expression Generated by Predicate Builder
// f => ((False OrElse Invoke(obj => (obj.Bar == "Hello"), f)) OrElse Invoke(obj => (obj.Qux == "Hello"), f))
void Main()
{
// creates a clause like
// select * from Menu where MenuText like '%ASD%' or ActionName like '%ASD%' or....
var items = Menu.Filter("ASD").ToList();
}
// Define other methods and classes here
public static class QueryExtensions
{
public static IQueryable<T> Filter<T>(this IQueryable<T> query, string search)
{
var properties = typeof(T).GetProperties().Where(p =>
/*p.GetCustomAttributes(typeof(System.Data.Objects.DataClasses.EdmScalarPropertyAttribute),true).Any() && */
p.PropertyType == typeof(String));
var predicate = PredicateBuilder.False<T>();
foreach (var property in properties )
{
predicate = predicate.Or(CreateLike<T>(property,search));
}
return query.AsExpandable().Where(predicate);
}
private static Expression<Func<T,bool>> CreateLike<T>( PropertyInfo prop, string value)
{
var parameter = Expression.Parameter(typeof(T), "f");
var propertyAccess = Expression.MakeMemberAccess(parameter, prop);
var like = Expression.Call(propertyAccess, "Contains", null, Expression.Constant(value,typeof(string)));
return Expression.Lambda<Func<T, bool>>(like, parameter);
}
}
You need to add reference to LinqKit to use PredicateBuilder and AsExpandable method
otherwise won't work with EF, only with Linq to SQL
If you want Col1 like '%ASD%' AND Col2 like '%ASD%' et, change PredicateBuilder.False to PredicateBuilder.True and predicate.Or to predicate.And
Also you need to find a way to distinguish mapped properties by your own custom properties (defined in partial classes for example)
I'd like to dynamically create a MemberAcess Expression to a deeper level then 1 (recursively):
public class Job
{
public string Name { get; set; }
public int Salary { get; set; }
}
public class Employee
{
public string Name { get; set; }
public Job Job { get; set; }
}
And I want to dynamically create a list of MemberAccesExpressions for each property in Employee and each property in Employee's complex members, the outcome should be something like this:
MemberAccesExpression[] {
{ e => e.Name },
{ e => e.Job.Name },
{ e => e.Job.Name }
}
This is a pseudo code of what I got:
List list = new List();
public Expression<Func<TModel, dynamic>> CreateME<TModel>(TModel model)
{
var type = typeof (TModel);
var properties = type.GetProperties();
foreach (var prop in properties)
{
// I want to ignore collections
if (typeof(ICollection).IsAssignableFrom(prop.PropertyType)) continue;
// Recall for complex property
if (prop.PropertyType.Namespace != "System")
{
// CreateME(model, ) // THIS IS WHEN I DON'T KNOW WHAT TO DO
continue;
}
var param = Expression.Parameter(type, "x");
var memberAccess = Expression.PropertyOrField(param, prop.Name);
list.Add(Expression.Lambda<Func<TModel, dynamic>>(memberAccess, param));
}
}
How do I make this a recursive method?
I thought of adding an optional parameter named
(TModel model, Expression> baseMemberAccess = null)
and somehow concat the member expression to baseMemberAccess if it's not null.
P.S.
is there's a better way to determine if a Type is not atomic type then this
(prop.PropertyType.Namespace != "System")
? (not int,float,string,etc...)
Appreciate any help,
Adam
An edit for trying to lay it more simply:
If I want to create an expression tree of a member access to Employee.Name this is what I do:
var param = Expression.Parameter(type, "x");
var memberAccess = Expression.PropertyOrField(param, memberName);
return Expression.Lambda<Func<TModel, TMember>>(memberAccess, param);
What is the equivalent to this for a member access to Employee.Job.Salary ?
public IEnumerable<Expression<Func<TModel, dynamic>>> CreateME<TModel>()
{
var stack = new Stack<StackItem>();
var type = typeof(TModel);
var parameterExpression = Expression.Parameter(type, "x");
stack.Push(new StackItem(typeof(TModel), parameterExpression));
while (stack.Count > 0)
{
var currentItem = stack.Pop();
var properties = currentItem.PropertyType.GetProperties();
foreach (var property in properties)
{
if (IsComplexProperty(property))
stack.Push(new StackItem(property.PropertyType, Expression.PropertyOrField(currentItem.AccessChainExpression, property.Name)));
else
{
yield return GetSimplePropertyExpression<TModel>(parameterExpression, currentItem.AccessChainExpression, property.Name);
}
}
}
}
private static Expression<Func<TModel, dynamic>> GetSimplePropertyExpression<TModel>(ParameterExpression lhs, Expression accessChain, string propertyName)
{
var memberAccess = Expression.Convert(Expression.PropertyOrField(accessChain, propertyName), typeof(object));
return Expression.Lambda<Func<TModel, dynamic>>(memberAccess, lhs);
}
private static bool IsComplexProperty(PropertyInfo p)
{
return !typeof (ICollection).IsAssignableFrom(p.PropertyType) && p.PropertyType.Namespace != "System";
}
class StackItem
{
public StackItem(Type propertyType, Expression accessChainExpression)
{
PropertyType = propertyType;
AccessChainExpression = accessChainExpression;
}
public Type PropertyType { get; private set; }
public Expression AccessChainExpression { get; private set; }
}
Im sure it can be improved, but this should work.