entity framework+ DayOfWeek

使用System.Linq.Dynamic (在这里管理https://github.com/kahanu/System.Linq.Dynamic ),我试图捕获在DateTime上找到的DayOfWeek字段,以便使用Entity Framework 6(或更高版本)进行聚合。

以前要求类似的东西,这有很大帮助, 动态Linq +entity framework:动态选择的日期时间修改

entity framework支持使用the获取DayOfWeek

 SqlFunctions.DatePart("dw", datetime?) 

或者我们可以用类似的东西做一些更需要的东西

 DbFunctions.DiffDays(date?, date?). 

理念:

让Linq的DayOfWeek成为实体

我发现这非常有趣,我喜欢它,因为它不使用可能让我局限于SQL Server的SqlFunctions 。 此外,开发人员可以控制一周的第一天,而无需查询SQL Server属性以查找其配置方式(第一天)。

出于实验目的,我一直在尝试在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); } 

使用上面的想法,我想我可以包装这个表达式,然后将模数值应用于输入时间,但我甚至不能让这个表达式更进一步。

我觉得我错过了构建表达式的基本部分。 我得到的错误

参数类型不匹配

在System.Linq.Expressions.Expression.Bind(MemberInfo成员,表达式表达式)
在System.Linq.Expressions.ExpressionVisitor.Visit [T](ReadOnlyCollection 1 nodes, Func 2 elementVisitor)
在System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression节点)
在System.Linq.Expressions.ExpressionVisitor.VisitLambda [T](表达式`1节点)
在System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression节点)
在System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider节点)
在System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression节点)
在Path \ QueryableExtensions.cs中的QueryableExtensions.DbFunctionsBinder.VisitMethodCall(MethodCallExpression节点):第48行
在Path \ QueryableExtensions.cs中的BindDbFunctions(IQueryable source):第13行
在Path \ AggregateHelper.cs中的AggregateHelper.d__15.MoveNext():第811行

—从抛出exception的先前位置开始的堆栈跟踪结束

在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)
在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)
在System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
在Path \ AggregationPluginServiceHelper.cs中的AggregationPluginServiceHelper.d__9.MoveNext():第199行

我知道我能做到这一点是我编写了内联编译的查询。 这很好。 但这是专门使用动态库。

示例用法是:

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

有没有更好的方式来获得一周的一天? 我知道这可能在文化上有所不同,所以做与星期日不同的日子模数(上面的链接想法)我认为是正确的方法。

所以你基本上需要转换表达式

 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> Expr(Expression> e) => e; public static Expression> Expr(Expression> e) => e; public static Expression> Expr(Expression> e) => e; public static Expression> Expr(Expression> 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(xp, xv)); } 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); } } } 

这与C#6静态导入function相结合,使实现更加简单易读。

例如:

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