I am trying to convert a DateTime to a String before calling contains on it. However despite my efforts of putting the result of one expression into another I fail miserably.
The code is derived from the highest answer to this question jqgrid with asp.net webmethod and json working with sorting, paging, searching and LINQ — but needs dynamic operators.
Assume I have the following method as StringExtension from the question:
public static class StringExtensions
{
public static MemberExpression ToMemberExpression(this string source, ParameterExpression p)
{
if (p == null)
throw new ArgumentNullException("p");
string[] properties = source.Split('.');
Expression expression = p;
Type type = p.Type;
foreach (var prop in properties)
{
var property = type.GetProperty(prop);
if (property == null)
throw new ArgumentException("Invalid expression", "source");
expression = Expression.MakeMemberAccess(expression, property);
type = property.PropertyType;
}
return (MemberExpression)expression;
}
}
Therefore I have the following method also from the question which I have then adapted for DateTime.
public virtual Expression<Func<T, bool>> CreateExpression<T>(string searchField, string searchString, string searchOper)
{
Expression exp = null;
var p = Expression.Parameter(typeof(T), "p");
Expression propertyAccess = searchField.ToMemberExpression(p);
switch (searchOper)
{
case "bw":
exp = Expression.Call(propertyAccess, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), Expression.Constant(searchString));
break;
// New code by me
case "cn":
if (propertyAccess.Type == typeof(DateTime))
{
// My faulty logic - from Jon Skeet answer below
Expression toStringCall = Expression.Call(
propertyAccess, "ToString",
null,
new[] { Expression.Constant("D") });
Expression containsCall = Expression.Call(
toStringCall, "Contains",
null,
new[] { Expression.Constant(searchString) });
exp = containsCall;
}
else
{
// Unchanged
exp = Expression.Call(propertyAccess, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(searchString));
}
break;
case "ew":
exp = Expression.Call(propertyAccess, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), Expression.Constant(searchString));
break;
case "gt":
exp = Expression.GreaterThan(propertyAccess, Expression.Constant(searchString, propertyAccess.Type));
break;
case "ge":
exp = Expression.GreaterThanOrEqual(propertyAccess, Expression.Constant(searchString, propertyAccess.Type));
break;
case "lt":
exp = Expression.LessThan(propertyAccess, Expression.Constant(searchString, propertyAccess.Type));
break;
case "le":
exp = Expression.LessThanOrEqual(propertyAccess, Expression.Constant(searchString, propertyAccess.Type));
break;
case "eq":
exp = Expression.Equal(propertyAccess, Expression.Constant(searchString.ToType(propertyAccess.Type), propertyAccess.Type));
break;
case "ne":
exp = Expression.NotEqual(propertyAccess, Expression.Constant(searchString, propertyAccess.Type));
break;
default:
return null;
}
return (Expression<Func<T, bool>>)Expression.Lambda(exp, p);
}
I get the following exception.
LINQ to Entities does not recognize the method 'System.String ToString(System.String)' method, and this method cannot be translated into a store expression.
I suspect you want something like this (fixed from previous version):
using System;
using System.Linq.Expressions;
public class Person
{
public DateTime DateOfBirth { get; set; }
}
public class Test
{
static void Main()
{
var expr = Foo<Person>("DateOfBirth", "1976");
Person p = new Person
{
DateOfBirth = new DateTime(1976, 6, 19)
};
Console.WriteLine(expr.Compile()(p));
}
static Expression<Func<T, bool>> Foo<T>(string propertyName,
string searchValue)
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
Expression property = Expression.Property(parameter, propertyName);
Expression toStringCall = Expression.Call(
property, "ToString",
null,
new[] { Expression.Constant("D") });
Expression containsCall = Expression.Call(
toStringCall, "Contains",
null,
new[] { Expression.Constant(searchValue) });
return Expression.Lambda<Func<T, bool>>(containsCall, parameter);
}
}
Note that the "null" values are to show that it's a non-generic method call.
try this code .....
Call ToExpression(...)...
Ex. ToExpression(null,Product.Name,"==","Test");
Here. Product.Name is nested property..
public static Expression<Func<T, bool>> ToExpression<T>(string andOrOperator, string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
{
Expression<Func<T, bool>> func = null;
try
{
ParameterExpression paramExpr = Expression.Parameter(typeof(T));
var arrProp = propName.Split('.').ToList();
Expression binExpr = null;
string partName = string.Empty;
arrProp.ForEach(x =>
{
Expression tempExpr = null;
partName = partName.IsNull() ? x : partName + "." + x;
if (partName == propName)
{
var member = NestedExprProp(paramExpr, partName);
var type = member.Type.Name == "Nullable`1" ? Nullable.GetUnderlyingType(member.Type) : member.Type;
tempExpr = ApplyFilter(opr, member, Expression.Convert(ToExprConstant(type, value), member.Type));
}
else
tempExpr = ApplyFilter("!=", NestedExprProp(paramExpr, partName), Expression.Constant(null));
if (binExpr != null)
binExpr = Expression.AndAlso(binExpr, tempExpr);
else
binExpr = tempExpr;
});
Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(binExpr, paramExpr);
if (expr != null)
innerExpr = (andOrOperator.IsNull() || andOrOperator == "And" || andOrOperator == "AND" || andOrOperator == "&&") ? innerExpr.And(expr) : innerExpr.Or(expr);
func = innerExpr;
}
catch { }
return func;
}
private static MemberExpression NestedExprProp(Expression expr, string propName)
{
string[] arrProp = propName.Split('.');
int arrPropCount = arrProp.Length;
return (arrPropCount > 1) ? Expression.Property(NestedExprProp(expr, arrProp.Take(arrPropCount - 1).Aggregate((a, i) => a + "." + i)), arrProp[arrPropCount - 1]) : Expression.Property(expr, propName);
}
private static Expression ToExprConstant(Type prop, string value)
{
if (value.IsNull())
return Expression.Constant(value);
object val = null;
switch (prop.FullName)
{
case "System.Guid":
val = value.ToGuid();
break;
default:
val = Convert.ChangeType(value, Type.GetType(prop.FullName));
break;
}
return Expression.Constant(val);
}
private static Expression ApplyFilter(string opr, Expression left, Expression right)
{
Expression InnerLambda = null;
switch (opr)
{
case "==":
case "=":
InnerLambda = Expression.Equal(left, right);
break;
case "<":
InnerLambda = Expression.LessThan(left, right);
break;
case ">":
InnerLambda = Expression.GreaterThan(left, right);
break;
case ">=":
InnerLambda = Expression.GreaterThanOrEqual(left, right);
break;
case "<=":
InnerLambda = Expression.LessThanOrEqual(left, right);
break;
case "!=":
InnerLambda = Expression.NotEqual(left, right);
break;
case "&&":
InnerLambda = Expression.And(left, right);
break;
case "||":
InnerLambda = Expression.Or(left, right);
break;
case "LIKE":
InnerLambda = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right);
break;
case "NOTLIKE":
InnerLambda = Expression.Not(Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right));
break;
}
return InnerLambda;
}
public static Expression<Func<T, object>> PropExpr<T>(string PropName)
{
ParameterExpression paramExpr = Expression.Parameter(typeof(T));
var tempExpr = Extentions.NestedExprProp(paramExpr, PropName);
return Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Lambda(tempExpr, paramExpr).Body, typeof(object)), paramExpr);
}
public static IQueryOver<T, T> OrderBy<T>(this IQueryOver<T, T> Collection, string sidx, string sord)
{
return sord == "asc" ? Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Asc : Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Desc;
}
public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}