Create a method which create dynamic expression - c#

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;
}

Related

C#: Search a list of defined queryable fields, different per model, with linq through generics (using entity framework)

*****Scroll down for final working solution*****
All of my Entity Framework models use partials, which implement my own IEntity interface:
public interface IEntity
{
int Status { get; set; }
int ID { get; set; }
}
This allows me to filter any Entity which implements this interface, based on the following function (simplified version):
public static IQueryable<T> FilterByStatus<T>(this IQueryable<T> query, int status) where T : class, IEntity
{
return query.Where(m => m.Status == status);
}
Now I want a function which names all of the properties, which I might want to perform a text query on. Let's say that implementation Foo of IEntity has 2 values (Bar and Baz) that I want to perform queries on.
I currently have:
public static IQueryable<Foo> FooSearch(this Entities context, string query)
{
IQueryable<Foo> result = context.Foo;
if (!String.IsNullOrEmpty(query))
{
result = result.Where(m =>
m.Bar.ToLower().IndexOf(query.ToLower()) >= 0 ||
m.Baz.ToLower().IndexOf(query.ToLower()) >= 0);
}
return result;
}
But I want to set it up in a more generic way. Something like:
public interface IEntity
{
int Status { get; set; }
int ID { get; set; }
string[] QueryableProperties { get; set; }
}
And some kind of implementation like (pseudocode):
public static IQueryable<T> GenericSearch(this IQueryable<T> query, string query) where T : class, IEntity
{
if (!String.IsNullOrEmpty(query))
{
query = query.Where(m =>
m[QueryableProperties[0]].ToLower().IndexOf(query.ToLower()) >= 0 ||
m[QueryableProperties[1]].ToLower().IndexOf(query.ToLower()) >= 0 ||
// .... //
m[QueryableProperties[QueryableProperties.Count - 1]].ToLower().IndexOf(query.ToLower()) >= 0)
}
return query;
}
How can I achieve this?
******Working Solution******
Search function:
public static class SearchFilter
{
private static Expression GetNestedPropertyExpression(Expression expression, string propertyName)
{
Expression body = expression;
foreach (var member in propertyName.Split('.'))
{
body = Expression.PropertyOrField(body, member);
}
return body;
}
private static Expression<Func<T, bool>> GetSearchExpression<T>(string[] propertyNames, string query)
{
var parameterExp = Expression.Parameter(typeof(T), "category");
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
MethodInfo toLowerMethod = typeof(string).GetMethod("ToLower", Type.EmptyTypes);
List<Expression> methodCalls = new List<Expression>();
foreach (string propertyName in propertyNames)
{
var propertyExp = GetNestedPropertyExpression(parameterExp, propertyName);
var queryValue = Expression.Constant(query.ToLower(), typeof(string));
var toLowerMethodExp = Expression.Call(propertyExp, toLowerMethod);
var containsMethodExp = Expression.Call(toLowerMethodExp, containsMethod, queryValue);
methodCalls.Add(containsMethodExp);
}
var orExp = methodCalls.Aggregate((left, right) => Expression.Or(left, right));
return Expression.Lambda<Func<T, bool>>(orExp, parameterExp);
}
public static IQueryable<T> Search<T>(this IQueryable<T> query, string property) where T : class, IEntity
{
var filterAttributes = typeof(T).GetCustomAttributes(
typeof(FilterableAttribute), true
).FirstOrDefault() as FilterableAttribute;
if (filterAttributes == null) {
return query;
}
var filterableColumns = filterAttributes.FilterableAttributes;
if (filterableColumns == null || filterableColumns.Count() == 0)
{
return query;
}
if (property == null)
{
return query;
}
return query.Where(GetSearchExpression<T>(filterableColumns, property));
}
}
Decorator (example: both a property of my model, and a nested property):
[Filterable(FilterableAttributes = new string[] {
nameof(Foo),
nameof(Bar) + "." + nameof(Models.MyConnectedModel.Baz)
})]
public partial class MyConnectedModel: IEntity
{
}
Nice question :)
Here's how you can do this:
static Expression<Func<T, bool>> GetExpression<T>(string[] propertyNames, string query)
{
var parameterExp = Expression.Parameter(typeof(T), "category");
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
MethodInfo toLowerMethod = typeof(string).GetMethod("ToLower",Type.EmptyTypes);
List<Expression> methodCalls = new List<Expression>();
foreach (string propertyName in propertyNames)
{
var propertyExp = Expression.Property(parameterExp, propertyName);
var queryValue = Expression.Constant(query.ToLower(), typeof(string));
var toLowerMethodExp = Expression.Call(propertyExp, toLowerMethod);
var containsMethodExp = Expression.Call(toLowerMethodExp, containsMethod, queryValue);
methodCalls.Add(containsMethodExp);
}
var orExp = methodCalls.Aggregate((left, right) => Expression.Or(left, right));
return Expression.Lambda<Func<T, bool>>(orExp, parameterExp);
}
And then you can use it like this (query is an IQueryable<MyEntity>)
query=query.Where(GetExpression<MyEntity>(queryableProperties,"SomeValue"));
IMO, it can be surprisingly easy:
IQueryable<T> GenericSearch<T>(this IQueryable<T> items, string query)
{
var queryableProperties = items
.First()
.GetType()
.GetProperties()
.Where(p => p.PropertyType == typeof(string))
.ToList();
return items.Where(i => queryableProperties.Any(p => ((string)p.GetValue(i)).Contains(query)));
}
This searches all properties of type string in a list of items.
It can be a class method, an extension method, a static function, it's easy to understand and works well. It can be extended to Fields or restricted to some interfaces easily.
Adjust to your liking.

Dynamically construct an expression tree

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);
}

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 });
}

Linq IQueryable Generic Filter

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)

Creating MemberAcces with ExpressionTree to a complex type

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.

Categories