Error in Create Dynamic Linq Query Expression - c#

PropertyInfo[] All_prop = Get_All_prop2(Model);
//------Get Properties has Value-----
foreach (PropertyInfo property in All_prop)
{
//--Check Has Value Property
if (property.GetValue(this, null) != null)
{
IQueryable<DAL_BankWebApp.TBUSER> Query = cntx.TBUSERs.Select(x => x);
//--Create Expression Of Property
ParameterExpression pe = Expression.Parameter(typeof(string), property.Name);
ConstantExpression Constant = Expression.Constant(property.GetConstantValue());
Expression Contain = Expression.Call(pe, typeof(string).GetMethod("Contains", new[] { typeof(string) }), Constant);
//----Create Where
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { Query.ElementType },
Query.Expression,
Expression.Lambda<Func<string, bool>>(Contain, new ParameterExpression[] { pe }));
IQueryable<DAL_BankWebApp.TBUSER> results = Query.Provider.CreateQuery<DAL_BankWebApp.TBUSER>(whereCallExpression);
Result = results;
}
}
i get below error At => if (property.GetValue(this, null) != null)
base {"Object does not match target type."} System.ApplicationException {System.Reflection.TargetException}

i Solved :
if (property.GetValue(Model, null) != null)

Related

Build MethodCallExpression for DateTime as string for DataTables Api table search

I have expression builder for DataTables Api page search which should parse DateTime? to string and check whether it contains value i.e. "11/" or "5"
private static List<Expression> GetDateTimeExpression(List<Expression> expressionList, IDataQuery queryAdapter, PropertyInfo property, ParameterExpression parameter)
{
var searchValue = queryAdapter.SearchObject.Value;
var memberExpression = Expression.Property(parameter, property.Name);
var memberExpValue = Expression.Property(memberExpression, "Value");
var methodInfo = typeof(DateTime).GetMethod("ToString", new[] { typeof(string) });
var toStringExp = Expression.Call(memberExpValue, methodInfo, Expression.Constant(null, typeof(string))); // Issue here as Constant value is always inserted
var methodInfo2 = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var methodCallExpression = Expression.Call(toStringExp, methodInfo2, Expression.Constant(searchValue, typeof(string)));
expressionList.Add(methodCallExpression);
return expressionList;
}
Current result:
p.DateCreated.Value.ToString(null).Contains("1") - which fails on .ToString(null) as null is not a valid in Linq Expression
Instead I should get:
p.DateCreated.Value.ToString().Contains("1")
Is there any way how to achieve this?

OrderBy with SwitchExpression in Linq to entities

I need make custom orderby for enum. I try use SwitchExpression:
public static IQueryable<T> MyOrderByEnum<T>(this IQueryable<T> source, string propName, Type enumType)
{
var type = typeof (T);
var property = type.GetProperty(propName);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.Property(parameter, property);
var enumValues = Enum.GetValues(enumType);
var switchCases = new SwitchCase[enumValues.Length];
int i = 0;
foreach (var val in enumValues)
{
switchCases[i] = Expression.SwitchCase(
Expression.Constant(val.ToDisplay()),
Expression.Constant(val)
);
i++;
}
var switchExpr1 =
Expression.Switch(
propertyAccess,
Expression.Constant(""),
switchCases
);
var orderByExp1 = Expression.Lambda(switchExpr1, parameter);
MethodCallExpression resultExp = Expression.Call(typeof (Queryable), "OrderBy", new[] {type, orderByExp1.Body.Type}, source.Expression, orderByExp1);
return (IOrderedQueryable<T>) source.Provider.CreateQuery(resultExp);
}
But when I execute
filtered.MyOrderBy("field1", typeof(FieldState)).ToList();
I get error:
Unknown LINQ expression of type 'Switch'.
Is there another way to make order expression that will translate into sql construction "CASE WHEN ..."?
Try Expression.Condition (https://msdn.microsoft.com/en-us/library/bb340500%28v=vs.110%29.aspx)
I think that translates to CASE When if used in an anonymous projection

How to implement specific OrderBy using expressions?

Try to implement custom linq query to order dataset by any field (first take rows, which field is equal to the search string, then take all others).
Final query should be like this:
source.OrderBy(p => p.Phone == "1234567")
Code:
var type = typeof(T);
var property = type.GetProperty(sortPropertyOrder.FieldName.ToString());
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
string methodName = sortPropertyOrder.SortDirection == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
Type[] typeArguments = new Type[] { type, property.PropertyType };
object fieldValue = Convert(property.PropertyType, sortPropertyOrder.FieldValue);
Expression equalExp = Expression.Equal(propertyAccess, Expression.Constant(fieldValue));
Expression finalExpression = Expression.Quote(Expression.Lambda(equalExp, parameter));
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, finalExpression);
IQueryable<T> query = source.Provider.CreateQuery<T>(resultExp);
As a result, I have an exception: No generic method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
Although I created more simple query(source.OrderBy(p => p.Phone))
var type = typeof(T);
var property = type.GetProperty(sortPropertyOrder.FieldName.ToString());
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
string methodName = sortPropertyOrder.SortDirection == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
Type[] typeArguments = new Type[] { type, property.PropertyType };
object fieldValue = Convert(property.PropertyType, sortPropertyOrder.FieldValue);
Expression finalExpression = Expression.Quote(Expression.Lambda(propertyAccess, parameter));
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, finalExpression);
IQueryable<T> query = source.Provider.CreateQuery<T>(resultExp);
It works fine.
What's wrong with first query?

Create dynamic LINQ expression for Select with FirstOrDefault inside

I am trying to write this groupQuery:
IQueryable<IGrouping<TKey, TEntity>> groupQuery;
...
IQueryable<TEntity> query2 = groupQuery.Select(x => x.FirstOrDefault());
as dynamically expression:
ParameterExpression param = Expression.Parameter(typeof(TEntity), "x");
IQueryable<TEntity> query2 = groupQuery.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
"Select",
new Type[] { typeof(TEntity), typeof(TKey)},
groupQuery.Expression,
Expression.Lambda(firstOrDefaultExpression, param)));
How to write firstOrDefaultExpression and how to complete this dynamically expression for same result as groupQuery.Select(x => x.FirstOrDefault())?
That should do it :
public IQueryable<TEntity> SelectFirst<TEntity,TKey>(IQueryable<IGrouping<TKey, TEntity>> groupQuery)
{
ParameterExpression param = Expression.Parameter(groupQuery.ElementType, "x");
var lambda = Expression.Lambda<Func<IGrouping<TKey, TEntity>,TEntity>>(
Expression.Call(typeof(Enumerable),"FirstOrDefault",new []{typeof(TEntity)},param),
param);
var select = Expression.Call(
typeof(Queryable),
"Select",
new []{groupQuery.ElementType,typeof(TEntity)},
groupQuery.Expression,lambda);
return groupQuery.Provider.CreateQuery<TEntity>(select);
}
And then you call that as :
SelectFirst(list.GroupBy(x=>[...]).AsQueryable());

How to construct LambdaExpression with conversion

I need to sort in ajax response grid by column name. Column value is number stored as a string.
Let's say some trivial class (in real-life situation there is no possibility to modify this class):
class TestObject
{
public TestObject(string v)
{
this.Value = v;
}
public string Value { get; set; }
}
then simple test:
[Test]
public void LambdaConstructionTest()
{
var queryable = new List<TestObject>
{
new TestObject("5"),
new TestObject("55"),
new TestObject("90"),
new TestObject("9"),
new TestObject("09"),
new TestObject("900"),
}.AsQueryable();
var sortingColumn = "Value";
ParameterExpression parameter = Expression.Parameter(queryable.ElementType);
MemberExpression property = Expression.Property(parameter, sortingColumn);
//// tried this one: var c = Expression.Convert(property, typeof(double));
LambdaExpression lambda = Expression.Lambda(property, parameter); //// constructs: o=>o.Value
var callExpression = Expression.Call(typeof (Double), "Parse", null, property);
var methodCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] { queryable.ElementType, property.Type },
queryable.Expression,
Expression.Quote(lambda)); // works, but sorts by string values.
//Expression.Quote(callExpression)); // getting: System.ArgumentException {"Quoted expression must be a lambda"}
var querable = queryable.Provider.CreateQuery<TestObject>(methodCallExpression);
// return querable; // <- this is the return of what I need.
}
Sorry for not being clear in my first post as #SLaks answer was correct but I do not know how to construct correct lambda expression in this case.
Finally found solution that is good for anyone who has strings in column and needs to sort by converted double value: (Special thanks for #SLaks his post was an eye opener):
[Test]
public void LambdaConstructionTest2()
{
// GIVEN
var queryable = new List<TestObject>
{
new TestObject("5"),
new TestObject("55"),
new TestObject("90"),
new TestObject("9"),
new TestObject("09"),
new TestObject("900"),
}.AsQueryable();
var sortingColumn = "Value";
// WHEN
ParameterExpression parameter = Expression.Parameter(queryable.ElementType);
MemberExpression property = Expression.Property(parameter, sortingColumn);
MethodCallExpression callExpression = Expression.Call(typeof (Double), "Parse", null, property);
LambdaExpression lambda = Expression.Lambda(callExpression, parameter); // = {Param_0 => Parse(Param_0.Value)}
UnaryExpression unaryExpression = Expression.Quote(lambda); // Expression<Func<TestObject,double>> = {Param_0 => Parse(Param_0.Value)}
var methodCallExpression = Expression.Call(
typeof (Queryable),
"OrderByDescending",
new[] { queryable.ElementType, lambda.ReturnType },
queryable.Expression,
unaryExpression);
var querable = queryable.Provider.CreateQuery<TestObject>(methodCallExpression);
// THEN
var expectedMaxValue = queryable.Max(x => Convert.ToDouble(x.Value));
var expectedMinValue = queryable.Min(x => Convert.ToDouble(x.Value));
var list = querable.ToList();
var actualMaxValue = Convert.ToDouble(list.First().Value);
var actualMinValue = Convert.ToDouble(list.Last().Value);
Assert.AreEqual(expectedMaxValue, actualMaxValue);
Assert.AreEqual(expectedMinValue, actualMinValue);
}
You can call Expression.Call() to create an expression node that calls a method.
Expression.Call(typeof(Double), "Parse", null, property)

Categories