I'm just learning about Expression and their expression-trees to use them with IronPython (but that's irrelevant for now).
What I'm trying to do is, creating an expression tree like the following lambda:
Func<T, int, string> func = (s,t) => s + t;
My current function is this:
public static Expression<Func<T, int, string>> StringConcatSelector<T>()
{
var parameterParam = Expression.Parameter(typeof(T), "x");
var paramToString = typeof(T).GetMethods().FirstOrDefault(s=>s.Name=="ToString");
var parameter = Expression.Call(parameterParam, paramToString);
var intParameterParam = Expression.Parameter(typeof(int), "s");
var intParameterToString = typeof(int).GetMethods().FirstOrDefault(s => s.Name == "ToString");
var intParameter = Expression.Call(intParameterParam, intParameterToString);
var stringConcat = typeof(string).GetMethods().FirstOrDefault(s => s.Name == "Concat");
var result = Expression.Call(stringConcat, parameter, intParameter);
return Expression.Lambda<Func<T, int, string>>
(result, parameterParam, intParameterParam);
}
the Expression.Callof String.Concat won't work this way, because of invalid parameter-count.
So I think I need something like:
create a List<string>-variable-expression
add both values to the list
use String.Concatwith the list-expression.
Am I right?
If yes, how can I create a List-variable (or an Array), add both values to take it as parameter for my String.Concat?
String.Concat method has 11 (!) overloads, and you are taking a random one.
The most appropriate for your case is
public static String Concat(String str0, String str1)
which you can get by using the following Type.GetMethod overload
public MethodInfo GetMethod(string name, Type[] types)
where the types array represents the type of the method arguments:
var stringConcat = typeof(string).GetMethod("Concat",
new[] { typeof(string), typeof(string) });
Related
I'm building a SQL "Any" clause dynamically using the System.Linq.Expressions.Expression class
I can do it like this
Expression<Func<User, Lead, bool>> predicate = (user, lead) => user.UserRoleSubProducts.Any(x => x.SubProductID == lead.SubProductID);
But I am not able to achieve this using Expression Tree.
I have tried below
var param1 = Expression.Parameter(typeof(User), "user");
var property1 = Expression.Property(param1, "UserRoleSubProducts");
var exp1 = Expression.Lambda(property1, new[] { param1 });
var param2 = Expression.Parameter(typeof(Lead), "lead");
var property2 = Expression.Property(param2, "SubProductID");
var exp2 = Expression.Lambda(property2, new[] { param2 });
var param3 = Expression.Parameter(property1.Type.GetProperty("Item").PropertyType, "x");
var property3 = Expression.Property(param3, "SubProductID");
var exp3 = Expression.Lambda(property3, new[] { param3 });
var equality = Expression.Equal(property2, property3);
var any = typeof(Queryable).GetMethods().Where(m => m.Name == "Any").Single(m => m.GetParameters().Length == 2).MakeGenericMethod(property1.Type);
var expression = Expression.Call(null, any, property1, equality);
But getting
Expression of type
'Microsoft.OData.Client.DataServiceCollection1[Api.Models.UserRoleSubProduct]' cannot be used for parameter of type System.Linq.IQueryable1[Microsoft.OData.Client.DataServiceCollection1[Api.Models.UserRoleSubProduct]]' of method 'Boolean Any[DataServiceCollection1](System.Linq.IQueryable1[Microsoft.OData.Client.DataServiceCollection1[Api.Models.UserRoleSubProduct]],
System.Linq.Expressions.Expression1[System.Func2[Microsoft.OData.Client.DataServiceCollection`1[Api.Models.UserRoleSubProduct],System.Boolean]])'
I think I am close enough. Any help is appreciated
Ignoring the redundant unused lambda expressions, the problem is in the last 2 lines.
First, you are using a wrong generic type (MakeGenericMethod(property1.Type)), while the correct type is basically the type of the parameter x here
.Any(x => x.SubProductID == lead.SubProductID)
=>
.Any<T>((T x) => ...)
which maps to param3.Type in your code.
Second, the second argument of the Any must be lambda expression (not simply equality as in the code).
Third, since user.UserRoleSubProducts most likely is a collection type, you should emit call to Enumerable.Any rather than Queryable.Any.
The Expression.Call method has overload which is very handy for "calling" static generic extension methods:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
So the last two lines can be replaced with:
var anyCall = Expression.Call(
typeof(Enumerable), nameof(Enumerable.Any), new Type[] { param3.Type },
property1, Expression.Lambda(equality, param3)
);
My objective is to create a subquery expression tree for a dynamic Full Text Search. In SQL it would be the equivalent of
SELECT *
FROM MV
WHERE MV.ID IN (SELECT ID
FROM MVF
WHERE title = "foo" OR Description = "foo")
So the basic idea is to create the FTS subquery, get the ids from that and use those for the In predicate. My issue is with the second part of that.
// Get subquery for FTS tables
ParameterExpression ftsParam = Expression.Parameter(typeof(MVF), "mvfts");
var wphrase = Expression.Constant("foo");
var methodInfo = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
var ftsID = Expression.Property(ftsParam, "ID");
var ftsTitle = Expression.Property(ftsParam, "Title");
var ftsDescrip = Expression.Property(ftsParam, "Description");
var texp = Expression.Call(ftsTitle, methodInfo, wphrase);
var dexp = Expression.Call(ftsDescrip, methodInfo, wphrase);
var ftsExp = Expression.Or(texp, dexp);
// Now get ids from the above fts resultset
// THE ASSIGNMENT BELOW THROWS
var selectExp = Expression.Call(typeof(IEnumerable<MVF>), "Select", new Type[]
{
typeof(long)
},
ftsExp,
Expression.Lambda<Func<MFV, long>>(
ftsID,
ftsParam
)
);
// Now set up MV table reference
ParameterExpression vParam = Expression.Parameter(typeof(MV), "mv");
var mvID = Expression.Property(vParam, "MVID");
var containsInfo = typeof(IEnumerable<long>).GetMethod("Contains", new Type[] { typeof(long) });
// Now combine expression to get those mvs with ids in the result set of fts query
var containsExp = Expression.Call(selectExp, containsInfo, mvID);
return Expression.Lambda<Func<MV, bool>>(containsExp, vParam);
Exception is:
No generic method 'Select' on type
'System.Collections.Generic.IEnumerable`1[MVF]' is compatible with the
supplied type arguments and arguments. No type arguments should be
provided if the method is non-generic.
Both methods needed by the expression in question are static generic extension methods (with the most important being static and generic) of the Enumerable class:
Enumerable.Select
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector
)
Enumerable.Contains
public static bool Contains<TSource>(
this IEnumerable<TSource> source,
TSource value
)
The most convenient way of "calling" such methods is the following Expression.Call method overload:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
The Type type argument is the type of the class defining the method being called (typeof(Enumerable) in this case) and the Type[] typeArguments is the array with the types of the generic type arguments (empty for non generic methods, should be { typeof(TSource), typeof(TResult) } for Select and { typeof(TSource) } for Contains).
Applying it to your scenario:
var selectExp = Expression.Call(
typeof(Enumerable),
"Select",
new { typeof(MFV), typeof(long) },
ftsExp,
Expression.Lambda<Func<MFV, long>>(ftsID, ftsParam)
);
and
var containsExp = Expression.Call(
typeof(Enumerable),
"Contains",
new [] { typeof(long) },
selectExp,
mvID
);
typeof(IEnumerable<long>) does not define a select method. The Linq Select Methods are Extension Methods not visible directly via reflection. The class that defines them is Enumerable. However typeof(Enumerable) will not work either as the method is generic. You have to get the generic method from the class enumerable first and then use MethodInfo.MakeGenericMethod to create a method which takes a long parameter.
var method = typeof(Enumerable).GetMethods().First(m => m.Name == "Select" &&
m.GetParameters().Last().ParameterType.GetGenericArguments().Length == 2);
var genericSelectFromLongToLong = method.MakeGenericMethod(new Type[] {typeof(long), typeof(long)});
Note that it would probably be better to check the generic arguments instead of the argument count for a match.
I am using dynamic Linq for generic search.
I have list of Ids:
List<int> idList = new List<int> { 1, 5, 6};
In plain Linq, I would write:
q = q.Where(a => idList.Contains(a.MyId));
But now I have to use System.Linq.Dynamic because I don't know in advance name of the column.
string someId = "CustomId";
q = q.Where("#0"+ ".Contains(" + someId + ")", idList.ToArray());
But this gives error:
"No applicable method 'Contains' exists in type 'Int32'"
How can I achieve this?
Is there some extension library that implements Contains for dynamic Linq or some other way.
You could write something like this that builds your query function dynamically:
public static Func<ObjT, bool> PropertyCheck<ObjT, PropT>(string propertyName, Expression<Func<PropT, bool>> predicate)
{
var paramExpr = Expression.Parameter(typeof(ObjT));
var propExpr = Expression.Property(paramExpr, propertyName);
return Expression.Lambda<Func<ObjT, bool>>(Expression.Invoke(predicate, propExpr), paramExpr).Compile();
}
Then, it could be used like this:
foos.Where(PropertyCheck<Foo, int>("MyId", x => idList.Contains(x)));
Of course, you could also just provide your own Where extension method that does all that at once:
public static IEnumerable<T> Where<T, PropT>(this IEnumerable<T> self, string propertyName, Expression<Func<PropT, bool>> predicate)
{
var paramExpr = Expression.Parameter(typeof(T));
var propExpr = Expression.Property(paramExpr, propertyName);
return self.Where<T>(Expression.Lambda<Func<T, bool>>(Expression.Invoke(predicate, propExpr), paramExpr).Compile());
}
foos.Where<Foo, int>("MyId", x => idList.Contains(x));
You could use the expressions to do this dynamic query, try something like this, for sample:
import these namespaces:
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
And try this:
// a reference parameter
var x = Expression.Parameter(typeof (YourType), "x");
// contains method
var containsMethod = typeof (string).GetMethod("Contains", new[] {typeof (string)});
// reference a field
var fieldExpression = Expression.Property(instance, "PropertyName");
// your value
var valueExpression = Expression.Constant(yourId);
// call the contains from a property and apply the value
var containsValueExpression = Expression.Call(fieldExpression, containsMethod, valueExpression);
// create your final lambda Expression
var filterLambda = Expression.Lambda<Func<YourType, bool>>(containsValueExpression, x);
// apply on your query
q = q.Where(finalLambda);
Obs: make sure your property has a method called contains.
If you look at the source of Dynamic LINQ then you can see that parsing in many cases depends on variable predefinedTypes.
In your case you need change this variable like this
static readonly Type[] predefinedTypes = {
....
,typeof(List<int>)
};
after that next code will be work
List<int> idList = new List<int> { 1, 5, 6};
....
string someId = "CustomId";
q = q.Where("#0.Contains(" + someId + ")", idList);
#Felipe Oriani in his 90% answer used the string.Contains method and the yourId single value, but asked was:
q = q.Where(a => idList.Contains(a.MyId));
which is member (property) access to the a.
So here is the final tested extension method:
/// <summary>
/// Creates lambda expression predicate: (TEntity entity) => collection.Contains(entity.property)
/// </summary>
public static Expression<Func<TEntity, bool>> ContainsExpression<TEntity, TProperty, TCollection>(
this TCollection collection,
Expression<Func<TEntity, TProperty>> property
)
where TCollection : ICollection<TProperty>
{
// contains method
MethodInfo containsMethod = typeof(TCollection).GetMethod(nameof(collection.Contains), new[] { typeof(TProperty) });
// your value
ConstantExpression collectionInstanceExpression = Expression.Constant(collection);
// call the contains from a property and apply the value
var containsValueExpression = Expression.Call(collectionInstanceExpression, containsMethod, property.Body);
// create your final lambda Expression
Expression<Func<TEntity, bool>> result = Expression.Lambda<Func<TEntity, bool>>(containsValueExpression, property.Parameters[0]);
return result;
}
The example:
List<int> idList = new List<int> { 1, 5, 6 };
Expression<Func<MyEntity,int>> idExpression = entity => entity.Id;
var contains = idList.ContainsExpression(idExpression)
IQueryable<MyEntity> q = DbContext.Set<MyEntity>().Where(contains);
Another way to skin this cat would be to convert the contains to ORs.
someArray.Constains(someField) is equivalent to:
someField == someArray[0] or someField == someArray[1] and so on.
It's not ideal but if the array is small it could work.
I started with the IQueryable extension methods from this example on CodePlex.
What i believe i need is an IQueryable extension method to "Where", where the method signature looks like:
public static IQueryable<T> Where<T>(this IQueryable<T> source, string columnName, string keyword)
and effectively does this (assuming T.columnName is of type string):
source.Where(p => p.ColumnName.Contains("keyword"))
using the above CodePlex example, i think i understand how he got the OrderBy method working, but my problem seems a bit more complex and I don't know how to get the Contains("keyword") part working.
Thanks in advance,
--Ed
Update: 9/13/2010 6:26pm PST
I thought the following would work, but end up getting a NotSupportedException (The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.) when I execute the expression via Count(). Any ideas?
public static IQueryable<T> Where<T>(this IQueryable<T> source, string columnName, string keyword)
{
var type = typeof(T);
var property = type.GetProperty(columnName);
if (property.PropertyType == typeof(string))
{
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var sel = Expression.Lambda<Func<T, string>>(propertyAccess, parameter);
var compiledSel = sel.Compile();
return source.Where(item => compiledSel(item).Contains(keyword));
}
else
{
return source;
}
}
public static IQueryable<T> Where<T>(
this IQueryable<T> source, string columnName, string keyword)
{
var arg = Expression.Parameter(typeof(T), "p");
var body = Expression.Call(
Expression.Property(arg, columnName),
"Contains",
null,
Expression.Constant(keyword));
var predicate = Expression.Lambda<Func<T, bool>>(body, arg);
return source.Where(predicate);
}
Well a couple of years later, but if anybody still need this, here it is:
public static IQueryable<T> Has<T>(this IQueryable<T> source, string propertyName, string keyword)
{
if (source == null || propertyName.IsNull() || keyword.IsNull())
{
return source;
}
keyword = keyword.ToLower();
var parameter = Expression.Parameter(source.ElementType, String.Empty);
var property = Expression.Property(parameter, propertyName);
var CONTAINS_METHOD = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var TO_LOWER_METHOD = typeof(string).GetMethod("ToLower", new Type[] { });
var toLowerExpression = Expression.Call(property, TO_LOWER_METHOD);
var termConstant = Expression.Constant(keyword, typeof(string));
var containsExpression = Expression.Call(toLowerExpression, CONTAINS_METHOD, termConstant);
var predicate = Expression.Lambda<Func<T, bool>>(containsExpression, parameter);
var methodCallExpression = Expression.Call(typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
I called my method "Has" just to keep it short, sample usage:
filtered = filtered.AsQueryable().Has("City", strCity)
And you can concatenate to make it even more expressive:
filtered = filtered.AsQueryable().Has("Name", strName).Has("City", strCity).Has("State", strState);
By the way, the "IsNull()" attached to the strings is just another simple extension method:
public static Boolean IsNull(this string str)
{
return string.IsNullOrEmpty(str);
}
The .Contains("keyword") part is exactly right in your example.
It's the p.ColumnName part that's going to cause trouble.
Now, there are a number of ways of doing this, generally involving either reflection or Expression<>, neither of which is particularly efficent.
The problem here is by passing the column name as a string, you are doing to undo that exact thing LINQ was invented to allow.
However, there are probably better ways of accomplishing your overall task besides that way.
So, let's look at alternate ways:
You want to be able to say :
var selector = new Selector("Column1", "keyword");
mylist.Where(item => selector(item));
and have it was the equivalent of
mylist.Where(item=> item.Column1.Contains("keyword"));
How 'bout we go with:
Func<MyClass, string> selector = i => i.Column1;
mylist.Where(item => selector(item).Contains("keyword"));
or
Func<MyClass, bool> selector = i => i.Column1.Contains("keyword");
mylist.Where(item => selector(item));
These are easily expanded for alternatives:
Func<MyClass, string> selector;
if (option == 1)
selector = i => i.Column1;
else
selector = i => i.Column2;
mylist.Where(item => selector(item).Contains("keyword"));
See, in generics type of the object works dynamically. So when p.ColumnName is taken as string, the Contains of string is been executed.
Generally for any lambda expression you specify, it parses the thing into an Expression and ultimately produces the output at runtime.
If you see my post :
http://www.abhisheksur.com/2010/09/use-of-expression-trees-in-lamda-c.html
you will understand how it works actually.
Consider this:
var propertyinfo = typeof(Customer).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;
now I want to declare
Func<int,orderType>
I know its not possible directly since ordertype is at runtime but is there is any workaround ?
this is exactly what I want to do :
var propertyinfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;
var param = Expression.Parameter(typeof(T), "x");
var sortExpression = (Expression.Lambda<Func<T, orderType>>
(Expression.Convert(Expression.Property(param, sortExpressionStr), typeof(orderType)), param));
all this because I want to convert:
Expression<Func<T,object>> to Expression<Func<T,orderType>>
or if its not possible then I want to create it from the first place with the right type , the case is as following:
I'm inside a method which have a type(Customer) and a property name of that type I want to order by it , I want to create a sort expression tree to pass it to Orderby (here).
You can do this by using an open generic type definition, and then making the specific type from that:
typeof(Func<,>).MakeGenericType(typeof(int), orderType);
However, what you're trying to do (calling Lambda<TDelegate>) is not directly possible. You must call Lambda without a type parameter:
var propertyinfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;
var param = Expression.Parameter(typeof(T), "x");
var sortExpression = Expression.Lambda(
Expression.Convert(Expression.Property(param, sortExpressionStr),
orderType),
param));
This will create the proper Func<,> for you behind the scenes. If you want to compile the expression and use the delegate, you can only do this dynamically with
sortExpression.Compile().DynamicInvoke(param);
If you want to call the OrderBy extension method on Queryable, things get a little more complicated:
var propertyInfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyInfo.PropertyType;
// first find the OrderBy method with no types specified
MethodInfo method = typeof(Queryable).GetMethods()
.Where(m => m.Name == "OrderBy" && m.GetParameters().Length == 2)
.Single();
// then make the right version by supplying the right types
MethodInfo concreteMethod = method.MakeGenericMethod(typeof(T), orderType);
var param = Expression.Parameter(typeof(T), "x");
// the key selector for the OrderBy method
Expression orderBy =
Expression.Lambda(
Expression.Property(orderParam, propertyInfo),
orderParam);
// how to use:
var sequence = new T[0].AsQueryable(); // sample IQueryable
// because no types are known in advance, we need to call Invoke
// through relection here
IQueryable result = (IQueryable) concreteMethod.Invoke(
null, // = static
new object[] { sequence, orderBy });
You can use the Type.MakeGenericType Method:
Type result = typeof(Func<,>).MakeGenericType(typeof(int), orderType);
This should work:
public static IQueryable<T> OrderByField<T>(
IQueryable<T> q, string sortfield, bool ascending)
{
var p = Expression.Parameter(typeof(T), "p");
var x = Expression.Lambda(Expression.Property(p, sortfield), p);
return q.Provider.CreateQuery<T>(
Expression.Call(typeof(Queryable),
ascending ? "OrderBy" : "OrderByDescending",
new Type[] { q.ElementType, x.Body.Type },
q.Expression,
x));
}
From here.
linqClass.OrderBy(GetSortExpression(sortstr));
public static Expression<Func<T,object>> GetSortExpression<T>(string sortExpressionStr)
{
var param = Expression.Parameter(typeof(T), "x");
var sortExpression = Expression.Lambda<Func<T, object>>(Expression.Property(param, sortExpressionStr), param);
return sortExpression;
}
this worked my problem was i used to pass extra parameter Typeof(Object) and orderby used to tell me it cant sort by Object type. thanks all
thanks dtb i will check if your answer work too and i will accept it if it works if not i will accept thsi one.
You want to use Dynamic Linq, a part of the Visual Studio sample code.
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
See if my solution dynamic enough.
public class Product
{
public long ID { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
}
static void Main(string[] args)
{
List<Product> products = (from i in Enumerable.Range(1, 10)
select new Product { ID = i, Name = "product " + i, Date = DateTime.Now.AddDays(-i) }).ToList(); //the test case
const string SortBy = "Date"; // to test you can change to "ID"/"Name"
Type sortType = typeof(Product).GetProperty(SortBy).PropertyType; // DateTime
ParameterExpression sortParamExp = Expression.Parameter(typeof(Product), "p"); // {p}
Expression sortBodyExp = Expression.PropertyOrField(sortParamExp, SortBy); // {p.DateTime}
LambdaExpression sortExp = Expression.Lambda(sortBodyExp, sortParamExp); // {p=>p.DateTime}
var OrderByMethod = typeof(Enumerable).GetMethods().Where(m => m.Name.Equals("OrderBy") && m.GetParameters().Count() == 2).FirstOrDefault().MakeGenericMethod(typeof(Product), sortType);
var result = OrderByMethod.Invoke(products, new object[] { products, sortExp.Compile() });
}
Base on above, it's not difficult to change Product to T to make it generic.
You can get the Type associated with Func<int,orderType> in case you wanted to e.g. pass it into CreateDelegate.
But what are you ultimately wanting to do with it? There may be a more straightforward approach.