I'm actually working on a method that select object which at least one property contains one or multiple words.
The construction of the tree seems correct but The MethodCall fails.
Here's the code:
public List<Article> FilterArticle(string expression, ref List<Article> listeArticle)
{
IQueryable<Article> queryableData = listeArticle.AsQueryable();
string[] expressionList = expression.Split(new char[] { ' ' });
Linq.Expression predicate = null;
List<Linq.Expression> predicateList = new List<Linq.Expression>();
int i = 0;
foreach (PropertyInfo property in typeof(Article).GetProperties())
{
foreach (string ex in expressionList)
{
string propertyName = property.Name.ToString();
ParameterExpression parameterEx = Linq.Expression.Parameter(typeof(Article), "objectProperty");
MemberExpression propertyEx = Linq.Expression.Property(parameterEx, propertyName);
MethodInfo toStringMethod = typeof(object).GetMethod("ToString");
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var value = Linq.Expression.Constant(ex, typeof(string));
Linq.Expression containsMethodExp = Linq.Expression.Call(Linq.Expression.Call(propertyEx, toStringMethod), containsMethod, value);
predicateList.Add(containsMethodExp);
if (i == 0) { predicate = predicateList[i]; }
if (i > 0) predicate = Linq.Expression.Or(predicate, predicateList[i]);
i = i + 1;
}
}
Func<IEnumerable<Article>, Func<Article, bool>, IEnumerable<Article>> whereDelegate = Enumerable.Where;
MethodInfo whereMethodInfo = whereDelegate.Method;
// FAIL
MethodCallExpression methodCallEx = Linq.Expression.Call(whereMethodInfo, predicate);
return queryableData.Provider.CreateQuery<Article>(methodCallEx).ToList();
}
This line:
MethodCallExpression methodCallEx = Linq.Expression.Call(whereMethodInfo, predicate);
Send this error:
An exception of type 'System.ArgumentException' occurred in
System.Core.dll but was not handled in user code
Additional information: Incorrect number of arguments supplied for
call to method
'System.Collections.Generic.IEnumerable1[Colibri.Models.Article]
Where[Article](System.Collections.Generic.IEnumerable1[Colibri.Models.Article],
System.Func`2[Colibri.Models.Article,System.Boolean])
'
Thanks for you help.
Related
I am creating lambda expression for Iqueryable to get value from a collection but I want to convert that value to other datatype like int or decimal. So as I cannot use c# casting with Iqueryable so I have created user defined scalar function in sql and trying to access that in expression but it throws exception that the 'methodname' cannot be converted to sql expression.
public class Context
{
[DbFunction("dbo", "ConvertToDouble")]
public int? ConvertToDouble(string value)
{
var sql = $"set #result = dbo.[ConvertToDouble]('{value}')";
var output = new SqlParameter { ParameterName = #"result", DbType = DbType.Int32, Size = 16, Direction = ParameterDirection.Output };
var result = Database.ExecuteSqlCommand(sql, output);
return output.Value as int?;
}
}
private static Expression<Func<TSource, TDataType>> CreateLamdaExpression<TSource, TDataType>(string fieldName)
{
var parameterExpression = Expression.Parameter(typeof(TSource));
var collectionParameter = Expression.Property(parameterExpression, "CustomFieldValues");
var childType = collectionParameter.Type.GetGenericArguments()[0];
var propertyParameter = Expression.Parameter(childType, childType.Name);
var left = Expression.Property(propertyParameter, "Name");
var right = Expression.Constant(fieldName);
var innerLambda = Expression.Equal(left, right);
var innerFunction = Expression.Lambda(innerLambda, propertyParameter);
var method = typeof(Enumerable).GetMethods().Where(m => m.Name == "FirstOrDefault" && m.GetParameters().Length == 2).FirstOrDefault().MakeGenericMethod(typeof(CustomFieldValue));
var outerLambda = Expression.Call(method, Expression.Property(parameterExpression, collectionParameter.Member as System.Reflection.PropertyInfo), innerFunction);
var propertyGetter = Expression.Property(outerLambda, "Value");
if (typeof(TDataType) != typeof(object))
{
/var changeTypeCall = Expression.Call(Expression.Constant(Context), Context.GetType().GetMethod("ConvertToDouble", BindingFlags.Public | BindingFlags.Instance),
propertyGetter
);
Expression convert = Expression.Convert(changeTypeCall,
typeof(TDataType));
return Expression.Lambda<Func<TSource, TDataType>>(convert, new ParameterExpression[] { parameterExpression });
}
var result = Expression.Lambda<Func<TSource, TDataType>>(propertyGetter, new ParameterExpression[] { parameterExpression });
return result;
}
Im trying to apply a filter to a DataGrid in WPF and the filter property needs a Predicate
ex:
dataGrid1.Filter = p => p.A_Field_Table1.Contains(textBox.Text);
But my datagrid is being filled with reflection, so I only know the type of objects inside the datagrid at runtime.
Then I created a method that dynamically generates the Predicate< T > :
public static Predicate< T > GetPredicate< T >(string column, string valueP, T objSource, string table)
{
Type itemType = typeof(T);
ParameterExpression predParam = Expression.Parameter(itemType, "p");
Expression left = Expression.Property(predParam, itemType.GetProperty("A_" + column+ "_" + table));
var valueStr= Expression.Constant(valueP);
var typeOfStr = valueStr.Type;
var containsMethod = typeOfStr.GetMethod("Contains", new [] { typeof(string) });
var call = Expression.Call(left, containsMethod, valueStr);
Func< T, bool > function = (Func< T, bool >)Expression.Lambda(call, new[] { predParam }).Compile();
return new Predicate< T >(function);
}
Then call this function on the interface:
var dataGridItem = dataGrid.Items[0];
dataGrid1.Filter = Class_X.GetPredicate(columnRefName,textBox.Text,dataGridItem,tableRefName);
But the generic method is throwing an exception saying that the type T is type of "object", even if objSource is type of Model.TableName.
I read some tutorials saying that T could not be resolved at runtime, then I should use "dynamic" instead of generic types.
I already tried using the "dynamic" type but I get a exception while casting the Lambda expression to Func< dynamic, bool>. Saying that I can't convert from < Model.TableName , bool > to < System.Object , bool >.
Is there an easier way to filter a datagrid that was filled by reflection?
You can't use generics in this case. Filter is a Func<object, bool>, so:
public static Predicate<object> GetPredicate(string column, string valueP, object objSource, string table)
{
Type itemType = objSource.GetType();
ParameterExpression predParam = Expression.Parameter(typeof(object), "p");
Expression left = Expression.Property(Expression.Convert(predParam, itemType), "A_" + column+ "_" + table);
var valueStr= Expression.Constant(valueP);
var typeOfStr = valueStr.Type;
var containsMethod = typeOfStr.GetMethod("Contains", new [] { typeof(string) });
var call = Expression.Call(left, containsMethod, valueStr);
//To handle null values. It considers null == string.Empty
//var left2 = Expression.Coalesce(left, Expression.Constant(string.Empty));
//var call = Expression.Call(left2, containsMethod, valueStr);
//If you want null values to be distinct from string.Empty, it's
//much more complex. You'll need a temporary variable (left2)
//where to put the value of the property, and then you can use the
//Expression.Condition (that is the ? : ternary operator) to
//test for null values
//var left2 = Expression.Variable(typeof(string));
//var assign = Expression.Assign(left2, left);
//var notNull = Expression.NotEqual(left2, Expression.Constant(null));
//var call = Expression.Call(left2, containsMethod, valueStr);
//var condition = Expression.Condition(notNull, call, Expression.Constant(false));
//var block = Expression.Block(new[] { left2 }, new Expression[] { assign, condition });
Predicate<object> function = Expression.Lambda<Predicate<object>>(call, new[] { predParam }).Compile();
return function;
}
The "trick" is that in the returned function the parameter is casted to the "right" type (obtained by objSource.GetType())
Be aware that you aren't testing for null values in the property of the row (NullReferenceException if you try to use Contains on a null property)
Im trying to implement Dynamic LINQ Query for selected columns to show has Output columns in LINQ Query.
Here is the error:
/Property 'System.String CompanyCode' is not defined for type
'System.String'"
public static void SelectProjection()
{
DataMovementDataContext dbMovement = new DataMovementDataContext();
var entity = dbMovement.ListofAccountingDocs2_1075s.AsQueryable();
Type type = entity.ElementType;
var entityParam = Expression.Parameter(entity.ElementType, "row");
Expression expr = entityParam;
string[] props = "AccountingDocumentNbr,CompanyCode,FiscalYearNbr".Split(',');
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
// row => row.Property
// var columnLambda = Expression.Lambda( Expression.Property(entityParam, "GLCompanyCode"), entityParam);
var columnLambda = Expression.Lambda(Expression.Property(expr, "AccountingDocumentNbr,GLCompanyCode"), entityParam);
// Items.Select(row => row.Property)
var selectCall = Expression.Call(typeof(Queryable), "Select", new Type[] { entity.ElementType, columnLambda.Body.Type }, entity.Expression, columnLambda);
// Items.Select(row => row.Property).Distinct
var distinctCall = Expression.Call(typeof(Queryable), "Distinct", new Type[] { typeof(string) }, selectCall);
// colvalue => colvalue
var sortParam = Expression.Parameter(typeof(string), "AccountingDocumentNbr");
var columnResultLambda = Expression.Lambda(sortParam, sortParam);
// Items.Select(row => row.Property).Distinct.OrderBy(colvalue => colvalue)
var ordercall = Expression.Call(typeof(Queryable), "OrderBy",
new Type[] { typeof(string), columnResultLambda.Body.Type },
distinctCall, columnResultLambda);
var result = entity.Provider.CreateQuery(ordercall);
foreach (var item in result)
{
Console.Write(item);
}
}
Can any one provide me help in solving above error?
If we look at this code:
// I chnaged this part:
string[] props = new string[]
{
"AccountingDocumentNbr",
"CompanyCode",
"FiscalYearNbr"
};
Expression expr;
Type type = entity.ElementType;
foreach (string prop in props)
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType; // This line may cause your loop
// to do something you dont want to do?
}
It seems to me that your loop going down the properties, gets a string and then searches for a prop called CompanyCode, which the class String obviously doesnt have.
Your loop does the following at the moment:
from type get the property named AccountingDocumentNbr
from the return type of property AccountingDocumentNbr get the property called CompanyCode
form the return type of property CompanyCode get the type of the property called FiscalYearNbr
I hardly doubt this is what you really want to do.
I have the following code block I am using to perform some dynamic filtering on a generic IQueryable list
private static MethodInfo miTL = typeof(String).GetMethod("ToLower", Type.EmptyTypes);
public static IQueryable<T> Where<T>(IQueryable<T> source, string member, object value)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (member == null)
{
throw new ArgumentNullException("member");
}
if (value == null)
{
throw new ArgumentNullException("value");
}
if (value is string && !string.IsNullOrWhiteSpace(value.ToString()))
{
//If the type is string, force lowercase equals comparison for both sides.
value = value.ToString().ToLower();
var parameter = Expression.Parameter(typeof(T), "item");
var parameterProperty = Expression.Property(parameter, member);
//ToLower dynamic expression
var dynamicExpression = Expression.Call(parameterProperty, miTL);
var constantValue = Expression.Constant(value);
var equalsExpression = Expression.Equal(dynamicExpression, constantValue);
var lambdaExpression = Expression.Lambda<Func<T, bool>>(equalsExpression, parameter);
return source.Where(lambdaExpression);
}
else
{
var item = Expression.Parameter(typeof(T), "item");
var prop = Expression.Property(item, member);
var soap = Expression.Constant(value);
var equal = Expression.Equal(prop, soap);
var lambda = Expression.Lambda<Func<T, bool>>(equal, item);
return source.Where(lambda);
}
}
This all works fine - apart from the possibility of my source containing null values, which then returns a null reference exception.
It directly translates to (when the field is "Counterparty" :- {item => (item.Counterparty.ToLower() == "name of counterparty")}
What I actually need in Lambda expression form is :-
{item => !string.IsNullEmptyOrWhitespace(item.Counterparty) && (item.Counterparty.ToLower() == "name of counterparty")}
Any ideas how to achieve this dynamically?
--REVIEWED--
Here is the whole replacement code, using a much nicer string.Equals check
public static IQueryable<T> Where<T>(IQueryable<T> source, string member, object value)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (member == null)
{
throw new ArgumentNullException("member");
}
if (value == null)
{
throw new ArgumentNullException("value");
}
if (value is string && !string.IsNullOrWhiteSpace(value.ToString()))
{
var parameter = Expression.Parameter(typeof(T), "item");
var parameterProperty = Expression.Property(parameter, member);
var body =
Expression.AndAlso(
Expression.Not(
Expression.Call(typeof(string), "IsNullOrEmpty", null, parameterProperty)
),
Expression.Call(typeof(string), "Equals", null,
parameterProperty, Expression.Constant(value),
Expression.Constant(System.StringComparison.InvariantCultureIgnoreCase))
);
var body2 = Expression.Call(typeof(string), "Equals", null,
parameterProperty, Expression.Constant(value),
Expression.Constant(System.StringComparison.InvariantCultureIgnoreCase));
var lambdaExpression = Expression.Lambda<Func<T, bool>>(body, parameter);
return source.Where(lambdaExpression);
}
else
{
var item = Expression.Parameter(typeof(T), "item");
var prop = Expression.Property(item, member);
var soap = Expression.Constant(value);
var equal = Expression.Equal(prop, soap);
var lambda = Expression.Lambda<Func<T, bool>>(equal, item);
return source.Where(lambda);
}
}
A literal translation would be something like:
var body =
Expression.AndAlso(
Expression.Not(
Expression.Call(typeof(string), "IsNullOrWhiteSpace", null,
parameterProperty)
),
Expression.Equal(
Expression.Call(parameterProperty, "ToLower", null),
Expression.Constant("name of counterparty")
)
);
However, you would do well to look at the various string.Equals overloads. For example:
var body = Expression.Call(typeof(string), "Equals", null,
parameterProperty, Expression.Constant("name of counterparty"),
Expression.Constant(System.StringComparison.InvariantCultureIgnoreCase));
I am using the Entity Framework, ASP.NET and C#3.5
I borrowed the following code to make sorting possible using a sortExpression from a GridView instead of the property of an entity:
public static IEnumerable<T> Sort<T>(this IEnumerable<T> source, string sortExpression)
{
string[] sortParts = sortExpression.Split(' ');
var param = Expression.Parameter(typeof(T), string.Empty);
var property = Expression.Property(param, sortParts[0]);
var sortLambda = Expression.Lambda<Func<T, object>>(Expression.Convert(property, typeof(object)), param);
if (sortParts.Length > 1 && sortParts[1].Equals("desc", StringComparison.OrdinalIgnoreCase))
{
return source.AsQueryable<T>().OrderByDescending(sortLambda);
}
return source.AsQueryable<T>().OrderBy(sortLambda);
}
The problem is that LINQ to Entities does not support casting to object. Instead of Func, I need the actual return type instead of object. I worked out how to do it:
public static IEnumerable<T> Sort<T>(this IEnumerable<T> source, string sortExpression)
{
string[] sortParts = sortExpression.Split(' ');
var param = Expression.Parameter(typeof(T), string.Empty);
var property = Expression.Property(param, sortParts[0]);
// NEW CODE HERE
Type propertyType = property.Type;
Type lambdaType = typeof(Func<,>).MakeGenericType(typeof(T), propertyType);
var sortLambda = Expression.Lambda<Func<T, object>>(Expression.Convert(property, propertyType), param);
//var sortLambda = Expression.Lambda<Func<T, object>>(Expression.Convert(property, typeof(object)), param);
if (sortParts.Length > 1 && sortParts[1].Equals("desc", StringComparison.OrdinalIgnoreCase))
{
return source.AsQueryable<T>().OrderByDescending(sortLambda);
}
return source.AsQueryable<T>().OrderBy(sortLambda);
}
The problem now is that if I have an Int32, it will not cast to object, which is still the return type. I worked around it like this:
public static IEnumerable<T> Sort<T>(this IEnumerable<T> source, string sortExpression)
{
string[] sortParts = sortExpression.Split(' ');
var param = Expression.Parameter(typeof(T), string.Empty);
var property = Expression.Property(param, sortParts[0]);
// New code here
Type propertyType = property.Type;
Type lambdaType = typeof(Func<,>).MakeGenericType(typeof(T), propertyType);
// NEWEST CODE HERE
var sortLambda = Expression.Lambda(lambdaType, Expression.Convert(property, propertyType), param);
//var sortLambda = Expression.Lambda<Func<T, object>>(Expression.Convert(property, propertyType), param);
//var sortLambda = Expression.Lambda<Func<T, object>>(Expression.Convert(property, typeof(object)), param);
if (sortParts.Length > 1 && sortParts[1].Equals("desc", StringComparison.OrdinalIgnoreCase))
{
return source.AsQueryable<T>().OrderByDescending(sortLambda);
}
return source.AsQueryable<T>().OrderBy(sortLambda);
}
This however does not compile any longer. The error is:
The type arguments for method 'System.Linq.Enumerable.OrderByDescending(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
The problem is that I do not want to specify the type arguments explicitly.
Does anyone know how to work around this or anyhow infer the TResult type from "propertyType"?
The approach used here, with the refinement here, should do what you need. By necessity it uses reflection to do the guts in the middle, but it works. Note also that Expression.GetFuncType and Expression.GetActionType can avoid some of the work in your approach.