I have several autocomplete actions, one of them is listed below. Instead of writing different predicates for each autocomplete Where method, I have created an autoCompletePredicate. Since I have multiple autocompletes I am using Reflection to get the Property which is required for that specific AutoComplete and use that Property in my autoCompletePredicate.
I have following code which is working alright.
static string param1, param2;
static PropertyInfo[] properties;
static PropertyInfo prop1, prop2;
public IHttpActionResult GetAutComplete(string term, string dependent)
{
int pagerSize = 10;
properties = new MyObject().GetType().GetProperties();
prop1 = properties.Where(p => p.Name.ToUpper().Equals("PROP1")).FirstOrDefault();
prop2 = properties.Where(p => p.Name.ToUpper().Equals("PROP2")).FirstOrDefault();
param1 = term;
param2 = dependent;
return Json(context.MyObject.Where(autoCompletePredicate).Select(r => new { label = r.PROP1 }).Distinct().OrderBy(r => r.label).Take(pagerSize).ToList());
}
Func<MyObject, int, bool> autoCompletePredicate = (GF, index) =>
{
bool isFound = false;
string term, dependent;
term = prop1.GetValue(GF).ToString();
dependent = prop2.GetValue(GF).ToString();
var termFound = term.Contains(param1.ToUpper());
var dependentFound = String.IsNullOrEmpty(param2) ? true : dependent.Contains(param2.ToUpper());
isFound = termFound && dependentFound;
return isFound;
};
How can I change this code into Expression. I tried below code which compiled fine but at runtime I got the following error
public static Expression<Func<MyObject, bool>> AutoCompleteExpression()
{
return r => prop1.GetValue(r).ToString().Contains(param1.ToUpper()) && (String.IsNullOrEmpty(param2) ? true : prop2.GetValue(r).ToString().Contains(param2.ToUpper()));
}
"LINQ to Entities does not recognize the method 'System.Object
GetValue(System.Object)' method, and this method cannot be translated
into a store expression."
I looked at the following post which makes absolute sense, but I am not sure how I can use that example in my scenario (which is dynamically finding properties using Reflection).
Also, what I would like to know what can be advantage of using Expression vs Func (specially in my case)
You are trying to trying to execute Contains method on string and want that to be represented in the ExpressionTrees, following is the code you need:
Create String Extension method - Contains: (Case Insensitive)
public static class StringExtensions
{
public static bool Contains(this string source, string toCheck)
{
return source.IndexOf(toCheck, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
Create the AutoCompleteExpression method as follows, it returns Func<MyObject, bool>:
public static Func<MyObject, bool> AutoCompleteExpression()
{
// Create ParameterExpression
ParameterExpression parameterType = Expression.Parameter(typeof(MyObject), "object");
// Create MemberExpression for Columns
MemberExpression typeColumnProp1 = Expression.Property(parameterType, "PROP1");
MemberExpression typeColumnProp2 = Expression.Property(parameterType, "PROP2");
// Create MethoIndo
MethodInfo containsMethodInfo = typeof(StringExtensions).GetMethod("Contains",new[] { typeof(string), typeof(string) },null);
// Create ConstantExpression values
ConstantExpression constant1 = Expression.Constant(param1, typeof(string));
ConstantExpression constant2 = Expression.Constant(param2, typeof(string));
// Expression for calling methods
MethodCallExpression expression1 = Expression.Call(null, containsMethodInfo, typeColumnProp1, constant1);
MethodCallExpression expression2 = Expression.Call(null, containsMethodInfo, typeColumnProp2, constant2);
// Combine `MethodCallExpression` to create Binary Expression
BinaryExpression resultExpression = Expression.And(expression1,expression2);
// Compile Expression tree to fetch `Func<MyObject, bool>`
return Expression.Lambda<Func<MyObject, bool>>(resultExpression, parameterType).Compile();
}
It is possible to add lot more flexibility by defining custom extension methods and combining expressions using And / Or
Related
Here is the problem:
I want to write extension method for getting property expression of collection field.
Here is an example.
I have the following class:
class Foo
{
public int Id {get; set;}
public List<Foo> Collection {get; set;}
}
And I have the following code:
Expression<Func<Foo, object>> expression = x => x.Collection.First().Id;
And I want to create an extension to get the same result expression with following signature:
Expression<Func<Foo, object>> expression = x => x.Collection.Field(foo => foo.Id);
static Func<Foo, object> Field(this List<Foo> people, Expression<Func<Foo, object>> accessor)
{
// implementation goes here
}
The main purpose of it is to generate path to property. In this case we need to get the following result: "Collection.Id". I already have method to convert Expression to property path. The Field method is an extension method, which I want. It should return the same result like x => x.Collection.First().Id but with the following signature: x => x.Collection.Field(foo => foo.Id)
Is there any way to do this?
Thanks a lot.
Since no methods are executed when assigning a lambda to an Expression variable, it isn't possible to have a method as you describe work.
What you can do is create a method that takes in the collection expression and the property expression and creates a new Expression lambda that has the desired result.
Expression<Func<T, TProp>> Field<T, TProp>(Expression<Func<T, IEnumerable<T>>> collectionEFn, Expression<Func<T, TProp>> pathEFn) {
// (Foo x)
var parm = collectionEFn.Parameters[0];
// x.Collection
var collectionExpr = collectionEFn.Body;
// y.Id
var pathExpr = (MemberExpression)pathEFn.Body;
// x.Collection.First()
var firstExpr = Expression.Call(typeof(Enumerable), "First", new[] { typeof(T) }, collectionExpr);
// x.Collection.First().Id
var newBody = Expression.Property(firstExpr, pathExpr.Member.Name);
// (Foo x) => x.Collection.First().Id
return Expression.Lambda<Func<T, TProp>>(newBody, parm);
}
With this method defined, you can call it as so:
var expr = Field((Foo x) => x.Collection, x => x.Id);
and the resulting value for expr will be an Expression tree representing x => x.Collection.First().Id
Note that the resulting type has the result type of the property referenced; if you want object, you would need to add a call to Convert to convert the PropertyExpression to the object type.
If you insist on doing so (like the others I am not still clear about your goal of doing this), you can create a generic extension method that takes a lambda expression as a parameter, and returns an expression. Something like this:
public static class MyExtensions
{
public static Expression<Func<T, object>> Field<T, TPropert>(
this IEnumerable<T> collection,
Expression<Func<T, TPropert>> field)
{
var prm = Expression.Parameter(typeof(IEnumerable<T>), "x");
var first = Expression.Call(typeof(Enumerable), "First", new[] { typeof(T) }, prm);
var body = Expression.Property(first, ((MemberExpression)field.Body).Member.Name);
return Expression.Lambda<Func<T, object>>(body, field.Parameters);
}
}
I have class DropdownFilter which has:
private readonly Func<TEntity, string> fieldWhichMustEqualValue;
public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
return filteredEntityCollection.Where(entity => this.fieldWhichMustEqualValue(entity) == value);
}
I use it as:
IQueryable<Invoice> entityCollectionToFilterAndOrder = ...
var dropdownFilter = new DropdownFilter<Invoice>(invoice => invoice.SomeProperty);
entityCollectionToFilterAndOrder = dropdownFilter.Filter(entityCollectionToFilterAndOrder, "bla bla bla");
which gives me
The LINQ expression node type 'Invoke' is not supported in LINQ to
Entities.
I understand the issue is that I am essentially asking for SQL equivalent of Invoke, which of course is wrong.
How should I rewrite the code? I understand that it needs to be an expression. My goal is the consumer of the DropDownFilter to just specify a property of TEntity, without providing the expression. i.e. the expression has to be encapsulated into the filter.
I have tried:
Expression<Func<TEntity, string>> expr = mc => this.fieldWhichMustEqualValue(mc);
Expression le = Expression.Equal(expr.Body, Expression.Constant(value));
var lambda = Expression.Lambda<Func<TEntity, bool>>(le, expr.Parameters);
return filteredEntityCollection.Where(lambda);
but it basically gives me the same result.
Func<TEntity, string> is a C# function so that cannot possibly be executed in the SQL server. However, if you change that to an expression, it could work:
private readonly Expression<Func<TEntity, string>> fieldWhichMustEqualValue;
However, now the Filter function would not compile since you can't invoke expressions. What you need to do is to compose a new expression:
public static UnaryExpression WrapInPropertyAccess(this ConstantExpression constant) {
Tuple<object> container = new Tuple<object>(constant.Value);
Expression containerExpression = Expression.Constant(container, typeof(Tuple<object>));
MemberExpression propAccess = Expression.Property(containerExpression, "Item1");
UnaryExpression result = Expression.Convert(propAccess, constant.Type); // Cast back the object to the value type
return result;
}
private Expression<TEntity, bool> FieldEqualsValue(string value) {
var comparison = Expression.Equal(fieldWhichMustEqualValue.Body, Expression.Constant(value).WrapInPropertyAccess());
return Expression.Lambda<Func<TEntity, bool>>(comparison, fieldWhichMustEqualValue.Parameters);
}
public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
var condition = FieldEqualsValue(value);
return filteredEntityCollection.Where(condition);
}
The WrapInPropertyAccess function is not strictly needed, but using it makes Entity Framework generate a parameterized expression.
I want to be able to build up an expression dynamically, which is essentially a property selector.
I am trying to use this so I can provide a flexible search UI and then translate the selected search parameters to an Entity Framework query.
I have most of what I need thanks to another library I am using, but am missing the final part which translates my query string parameters to the appropriate expression selector the other library requires.
The library takes an argument of :
Expression<Func<TObject, TPropertyType>>
An example of how this would be coded if baked into an application would be :
Expression<Func<MyObject, int>> expression = x=> x.IntegerProperty;
However, I need to be able to generate this expression dynamically, as the important point is that all I will know is the type of object (MyObject) and the property name as a string value ("IntegerProperty"). The property value will obviously map to an property on the object which could be of any non complex type.
So essentially I think I am wanting to find a way to build up the expression dynamically which specifies the correct object property to return and where the return value is determined by that property type.
psuedo code :
string ObjectPropertyName
Type ObjectType
Type ObjectPropertyType = typeof(ObjectType).GetProperty(ObjectPropertyName).Property
Expression<Func<[ObjectType], [ObjectPropertyType]>> expression = x=> x.[ObjectPropertyName];
Update :
I have got as far as this
ParameterExpression objectParameter = Expression.Parameter(type, "x");
MemberExpression objectProperty = Expression.Property(objectParameter, "PropertyNameString");
Expression<Func<ObjectType, int>> expression = Expression.Lambda<Func<ObjectType, int>>(objectProperty, objectParameter);
But the problem I have with this is that the return type is not always an int but may be some other type.
Doing what you asked is bit tricky but not impossible. Since the property type is not known until run time so you can not declare the Expression<Func<,>> so it would be done by reflection.
public static class QueryableExtension
{
public static object Build<Tobject>(this Tobject source, string propertyName)
{
var propInfo = typeof(Tobject).GetProperty(propertyName);
var parameter = Expression.Parameter(typeof(Tobject), "x");
var property = Expression.Property(parameter, propInfo);
var delegateType = typeof(Func<,>)
.MakeGenericType(typeof(Tobject), propInfo.PropertyType);
var lambda = GetExpressionLambdaMethod()
.MakeGenericMethod(delegateType)
.Invoke(null, new object[] { property, new[] { parameter } });
return lambda;
}
public static MethodInfo GetExpressionLambdaMethod()
{
return typeof(Expression)
.GetMethods()
.Where(m => m.Name == "Lambda")
.Select(m => new
{
Method = m,
Params = m.GetParameters(),
Args = m.GetGenericArguments()
})
.Where(x => x.Params.Length == 2
&& x.Args.Length == 1
)
.Select(x => x.Method)
.First();
}
}
Usage -
var expression = testObject.Build("YourPropertyName");
Now this will build the Expression you desired with return type of property. But since we don't know about your library but I suggest you to call your library method via reflection and pass the expression wrapped under object.
As I mentioned in the comments, building expression without knowing the property type is easy (even with nested property support):
static LambdaExpression MakeSelector(Type objectType, string path)
{
var item = Expression.Parameter(objectType, "item");
var body = path.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
return Expression.Lambda(body, item);
}
But then you'll need to find a way to call your generic library method - using reflection or dynamic call.
If you have both ObjectType and ObjectPropertyType as generic type parameters, you can use the Expression class to do something like this:
public static Expression<Func<TObject, TPropertyType>> Generate<TObject, TPropertyType>(
string property_name)
{
var parameter = Expression.Parameter(typeof (TObject));
return Expression.Lambda<Func<TObject, TPropertyType>>(
Expression.Property(parameter, property_name), parameter);
}
There is old intresting library DynamicLinq. May be it will be useful for you. It extends System.Linq.Dynamic to support execution of Lambda expressions defined in a string. With use of DynamicLinq you can do somethink like:
public class IndexViewModel
{
public bool HasPassword { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
//...........
Expression<Func<IndexViewModel, bool>> ex =
System.Linq.Dynamic.DynamicExpression.ParseLambda<IndexViewModel, bool>("TwoFactor");
var model = new ReactJs.NET.Models.IndexViewModel() { TwoFactor = true };
var res = ex.Compile()(model);
// res == true
System.Diagnostics.Debug.Assert(res);
I would like write a method which accepts two MemberExpression, and generates a delegate which accepts two objects - source and target, and assigns the value from the source - according to it's MemberExpression, to the field of the target, according to the second MemberExpression. The objects does not have to be of the same type.
I'm looking for something like this:
public Action<TSource, TTarget> Map(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var sourceField = getter.Body as MemberExpression;
var targetField = setter.Body as MemberExpression;
/*
* Now I would like to create a lambda expression which accepts TSource and TTarget instances,
* and assings TTarget according to the above getter and setter expressions. Kind of like:
* var assignExp = Expression.Assign(x, y);
* var lambda = Expression.Lambda<Action<TTarget, TSource>>( .... ).Compile();
* return lambda;
*/
}
Usage:
Target target;
Source source;
//...
var action = Map(p => p.NestedField.Dummy, x => x.TargetName);
action(source, target);
I don't understand how to build the expressions to send to Expression.Assign.
At this point, I don't mind about null values or initialization of fields. Please assume all fields are initialized.
Assign is used to generate assign expression, but in your case each lambda expression has own parameter, and both this parameters should be send to a new lambda expression.
So in my example i generate new assign expression, then create a new lambda expression, and send ParameterExpression from both getter and setter expressions to a new lambda.
So it should be like this:
Here is working sample - https://dotnetfiddle.net/uuPVAl and the code itself
using System;
using System.Linq.Expressions;
public class Program
{
public static void Main(string[] args)
{
Target target = new Target();
Source source = new Source()
{
NestedField = new NestedSource()
{
Dummy = "Hello world"
}
};
var action = Map<Source, Target>(p => p.NestedField.Dummy, x => x.TargetName);
action(source, target);
Console.WriteLine(target.TargetName);
}
public static Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var sourceField = getter.Body as MemberExpression;
var targetField = setter.Body as MemberExpression;
// here we create new assign expression
var assign = Expression.Assign(targetField, sourceField);
// and then compile it with original two parameters
var lambda = Expression.Lambda<Action<TSource, TTarget>>(assign, getter.Parameters[0], setter.Parameters[0]);
return lambda.Compile();
}
}
public class Target
{
public string TargetName { get; set; }
}
public class NestedSource
{
public string Dummy { get; set; }
}
public class Source
{
public NestedSource NestedField { get; set; }
}
UPDATE
So each Lambda Expression can have any parameters. From code side it's ParameterExpression. When you write expression as typical code, then it means function parameters, so in your case (p) => p.NestedField.Dummy - (p) is parameter of that function. And expression inside body uses it - p.NestedField.Dummy, so to be able to compile it - lambda expression needs to know that parameter.
In this case you have two lambda expressions for target and source, and each of them have own parameter - (p) and (x) and each expression use own parameter. But in result function we need to use both of them, as we have two parameters in the function, so we need to resend original ParameterExpression from source and target to a new lambda. Or you can create a new ParameterExpression but then you need to create a new tree as old one will use old ParameterExpression. Usually such things are done with ExpressionVisitor class, but in your case we can just resend original expressions without tree body changes.
This will do:
public Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var targetPropertyExpression = setter.Body as MemberExpression;
var targetProperty = targetPropertyExpression.Member as PropertyInfo;
return (src, tgt) => { targetProperty.SetValue(tgt, getter.Compile().Invoke(src)); };
}
It get's the property of the setter from the 1st lambda expression and just returns an action, which assigns the value to the property based on the 2nd lambda expression, which just needs to be invoked.
Take care of the <TSource, object> though, you maybe need an additional cast.
After a good dose of Googling and trying some things and not finding/getting the desired result I decided to post this question.
I have a custom made OrderBy extension method and now when performing the OrderBy operation I'd like to pass an AlphanumComparator like this:
return divergences.OrderBy(sort, new AlphanumComparator());
Here's the extension method:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection,
GridSortOptions sortOptions, AlphanumComparator comparer = null)
{
if (string.IsNullOrEmpty(sortOptions.Column))
{
return collection;
}
Type collectionType = typeof(T);
ParameterExpression parameterExpression = Expression.Parameter(collectionType, "p");
Expression seedExpression = parameterExpression;
Expression aggregateExpression = sortOptions.Column.Split('.').Aggregate(seedExpression, Expression.Property);
MemberExpression memberExpression = aggregateExpression as MemberExpression;
if (memberExpression == null)
{
throw new NullReferenceException(string.Format("Unable to cast Member Expression for given path: {0}.", sortOptions.Column));
}
LambdaExpression orderByExp = Expression.Lambda(memberExpression, parameterExpression);
const string orderBy = "OrderBy";
const string orderByDesc = "OrderByDescending";
Type childPropertyType = ((PropertyInfo)(memberExpression.Member)).PropertyType;
string methodToInvoke = sortOptions.Direction == MvcContrib.Sorting.SortDirection.Ascending ? orderBy : orderByDesc;
MethodCallExpression orderByCall;
orderByCall = Expression.Call(typeof(Queryable), methodToInvoke, new[] { collectionType, childPropertyType }, collection.Expression, Expression.Quote(orderByExp));
if(comparer != null)
{
// How can I pass the comparator to the OrderBy MethodCallExpression?
// Using the standard LINQ OrderBy, we can do this:
// elements.OrderBy(e => e.Index, new AlphanumComparator())
}
return collection.Provider.CreateQuery<T>(orderByCall);
}
See the comment in the code where I think I should pass the IComparer... how could I approach this?
I had to approach this differently.
I was trying to create a generic OrderBy to be used with MvcContrib Grid, but passing the IComparer to that custom OrderBy expression did not work as I imagined it would work.
So I created this helper that receives a string in dot notation like Element1.Standard.Chapter.Manual.Name and then returns an Expression<Func<T, string>>:
public static Func<T, string> CreateSelectorExpression<T>(string propertyName) where T : class
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
Expression aggregateExpression = propertyName.Split('.').
Aggregate(parameterExpression as Expression, Expression.Property) as MemberExpression;
LambdaExpression exp = Expression.Lambda(aggregateExpression, parameterExpression);
return (Func<T, string>)exp.Compile();
}
This expression typed to T (in this case Divergence object type) can then be passed (see func.Invoke) to the standard LINQ OrderBy operator where I can also pass the custom IComparer AlphanumComparator like this:
if (sort.Column.Contains("Index"))
{
var func = Helpers.ExtensionMethods.CreateSelectorExpression<Divergence>(sort.Column);
if (sort.Direction == SortDirection.Ascending)
{
return divergences.OrderBy(func, new AlphanumComparator());
}
else
{
return divergences.OrderByDescending(func, new AlphanumComparator());
}
}
This involved a little bit more work but solved the problem in a generic fashion the way I wanted it to be.