Entity Framework + DayOfWeek

Using System.Linq.Dynamic(managed here https://github.com/kahanu/System.Linq.Dynamic ), I am trying to capture a field DayOfWeekfound in DateTimefor aggregation purposes using Entity Framework 6 (or more).

They used to ask for something like this that really helped, Dynamic Linq + Entity Framework: datetime modifications for dynamic selection

Entity Framework supports fetching DayOfWeekwith

SqlFunctions.DatePart("dw", datetime?)

or we could do something more desirable using something like

DbFunctions.DiffDays(date?, date?).  

Idea:

Getting DayOfWeek in Linq for objects

I found this quite interesting, and I like it because it does not use SqlFunctions, which may prevent me from restricting myself to SQL Server. In addition, the developer controls that on the first day of the week, it is not necessary to query the SQL Server properties to find out how it is configured (for the first day).

For experimental purposes, I tried to implement this in an override VisitMember():

protected override Expression VisitMember(MemberExpression node)
{
     if (node.Type == typeof(System.DayOfWeek))
     {
          var firstSunday = new DateTime(1753, 1, 7);

                var firstSundayExpression = Expression.Constant(firstSunday, typeof(DateTime?));
                var timeValue = node.Expression;
                if (timeValue.Type != typeof(DateTime?)) timeValue = Expression.Convert(timeValue, typeof(DateTime?));
                var methodCall = Expression.Call(
                              typeof(DbFunctions), "DiffDays", Type.EmptyTypes, firstSundayExpression, timeValue);
                return Expression.Convert(methodCall, typeof(int?));
      }

      return base.VisitMember(node);
 }

Using the above idea, I think I can wrap this expression and then apply the value of the module to the input time, but I can not even get this expression for the future.

I feel that I am missing the fundamental part of how expressions are expressed. The error I get with this

Argument types do not match

System.Linq.Expressions.Expression.Bind( MemberInfo, Expression)
System.Linq.Expressions.ExpressionVisitor.Visit [T] (ReadOnlyCollection 1 nodes, Func 2 elementVisitor)
System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
System.Linq.Expressions.ExpressionVisitor.VisitLambda [T] (Expression`1 node)
System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
System.Linq.Expressions.ExpressionVisitor.VisitArguments( IArgumentProvider)
System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
QueryableExtensions.DbFunctionsBinder.VisitMethodCall(MethodCallExpression node) \QueryableExtensions.cs: 48
BindDbFunctions ( IQueryable) \QueryableExtensions.cs: 13
AggregateHelper.d__15.MoveNext() \AggregateHelper.cs: 811

--- ,

System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess( )
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification( )
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
AggregationPluginServiceHelper.d__9.MoveNext() \AggregationPluginServiceHelper.cs: 199

, , inline-compiled. . .

:

var grouping = select.GroupBy("new (DateTimeColumn.DayOfWeek)", "it");

? , , , ( ), .

+4
1

,

expr.DayOfWeek

var firstSunday = new DateTime(1753, 1, 7);
(DayOfWeek)(((int)DbFunctions.DiffDays((DateTime?)firstSunday, (DateTime?)expr)) % 7)

:

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);
}

:. , , :

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);
        }
    }
}

, # 6, .

:

using static System.Linq.Expressions.Expression;
using static ExpressionUtils;

:

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);
}

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);
}
+8

Source: https://habr.com/ru/post/1599404/


All Articles