So you basically need to convert an expression like
expr.DayOfWeek
to
var firstSunday = new DateTime(1753, 1, 7);
(DayOfWeek)(((int)DbFunctions.DiffDays((DateTime?)firstSunday, (DateTime?)expr)) % 7)
Here is how you can do that:
protected override Expression VisitMember(MemberExpression node)
{
if (node.Type == typeof(DayOfWeek))
{
var expr = node.Expression;
var firstSunday = new DateTime(1753, 1, 7);
var diffDays = Expression.Convert(
Expression.Call(
typeof(DbFunctions), "DiffDays", Type.EmptyTypes,
Expression.Constant(firstSunday, typeof(DateTime?)),
Expression.Convert(expr, typeof(DateTime?))),
typeof(int));
var dayOfWeek = Expression.Convert(
Expression.Modulo(diffDays, Expression.Constant(7)),
typeof(DayOfWeek));
return dayOfWeek;
}
return base.VisitMember(node);
}
Update: The process can be simplified by using a compile time prototype expressions, replacing the parameters with actual values using a small helper utility:
public static class ExpressionUtils
{
public static Expression<Func<T, TResult>> Expr<T, TResult>(Expression<Func<T, TResult>> e) => e;
public static Expression<Func<T1, T2, TResult>> Expr<T1, T2, TResult>(Expression<Func<T1, T2, TResult>> e) => e;
public static Expression<Func<T1, T2, T3, TResult>> Expr<T1, T2, T3, TResult>(Expression<Func<T1, T2, T3, TResult>> e) => e;
public static Expression<Func<T1, T2, T3, T4, TResult>> Expr<T1, T2, T3, T4, TResult>(Expression<Func<T1, T2, T3, T4, TResult>> e) => e;
public static Expression WithParameters(this LambdaExpression expression, params Expression[] values)
{
return expression.Parameters.Zip(values, (p, v) => new { p, v })
.Aggregate(expression.Body, (e, x) => e.ReplaceParameter(x.p, x.v));
}
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
This, combined with the C#6 static import feature makes the implementation much simpler and readable.
For instance:
using static System.Linq.Expressions.Expression;
using static ExpressionUtils;
The method in question now looks like this:
protected override Expression VisitMember(MemberExpression node)
{
if (node.Type == typeof(DayOfWeek))
{
return Expr((DateTime dateValue1, DateTime dateValue2) =>
(DayOfWeek)(DbFunctions.DiffDays(dateValue1, dateValue2).Value % 7))
.WithParameters(Constant(new DateTime(1753, 1, 7)), Visit(node.Expression));
}
return base.VisitMember(node);
}
and the one from your previous question about AddHours
:
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Object != null && node.Object.Type == typeof(DateTime))
{
if (node.Method.Name == "AddHours")
{
return Expr((DateTime timeValue, double addValue) =>
DbFunctions.AddHours(timeValue, (int)addValue).Value)
.WithParameters(Visit(node.Object), Visit(node.Arguments[0]));
}
}
return base.VisitMethodCall(node);
}