C# 我可以在实体框架中使用带有动态datePartArg的SqlFunctions.DateDiff()吗?
由于注释行,以下代码引发C# 我可以在实体框架中使用带有动态datePartArg的SqlFunctions.DateDiff()吗?,c#,entity-framework,exception,dynamic,datediff,C#,Entity Framework,Exception,Dynamic,Datediff,由于注释行,以下代码引发EntityCommandCompilationException: var datePartArg = "dd"; var minutesInStatePerSegment = await db.History_WorkPlaceStates .Where(x => selector.StartTimeUtc <= x.Started && x.Ended < selector.EndTimeUtc) .
EntityCommandCompilationException
:
var datePartArg = "dd";
var minutesInStatePerSegment = await db.History_WorkPlaceStates
.Where(x => selector.StartTimeUtc <= x.Started && x.Ended < selector.EndTimeUtc)
.Select(x => new {
start = x.Started,
minutes = x.Minutes,
state = x.State,
})
.GroupBy(x => new {
//This causes an exception:
segment = SqlFunctions.DateDiff(datePartArg, selector.StartTimeUtc, x.start),
state = x.state,
})
.Select(x => new {
state = x.Key.state,
segment = x.Key.segment,
minutes = x.Sum(y => y.minutes),
}).ToListAsync();
var datePartArg=“dd”;
var minutesInStatePerSegment=等待db.History_WorkPlaceStates
.Where(x=>selector.StartTimeUtc新{
开始=x.开始,
分钟=x.分钟,
state=x.state,
})
.GroupBy(x=>new{
//这会导致一个异常:
段=SqlFunctions.DateDiff(datePartArg,selector.starttimetc,x.start),
state=x.state,
})
.选择(x=>new{
state=x.Key.state,
段=x.Key.segment,
分钟=x.Sum(y=>y.minutes),
}).ToListAsync();
这是因为SQL Server中的DateDiff
只能将文字字符串用作其第一个参数,而不能使用变量。实体框架在SQL中生成一个变量,因此我们得到了异常
有没有办法解决这个问题?您可以创建以下扩展方法来解决这个问题。使用DateDiff时,只需使用此扩展方法替换GroupBy函数:
private static object GetMemberValue(MemberExpression member) {
var objectMember = Expression.Convert(member, typeof(object));
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
return getter();
}
public static IQueryable<IGrouping<TKey, TSource>> GroupByDateDiff<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector) {
var body = (NewExpression)keySelector.Body;
foreach (var arg in body.Arguments) {
if (arg.NodeType == ExpressionType.Call) {
var callNode = (MethodCallExpression)arg;
if (callNode.Method.Name == "DateDiff") {
var dateDiffFirstArg = callNode.Arguments[0];
if (dateDiffFirstArg.NodeType == ExpressionType.Constant) {
//It was already a constant, so we're good.
}
else {
//HACK: This will break if the internal implementation of ReadOnlyCollection changes.
var listInfo = typeof(ReadOnlyCollection<Expression>).GetField("list", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var list = (IList)listInfo.GetValue(callNode.Arguments);
if (dateDiffFirstArg.NodeType == ExpressionType.MemberAccess) {
list[0] = Expression.Constant((string)GetMemberValue((MemberExpression)dateDiffFirstArg));
}
else {
throw new ArgumentException($"{nameof(GroupByDateDiff)} was unable to parse the datePartArg argument to the DateDiff function.");
}
}
}
}
}
return source.GroupBy(keySelector);
}
私有静态对象GetMemberValue(MemberExpression成员){
var objectMember=Expression.Convert(成员,typeof(对象));
var getterLambda=Expression.Lambda(objectMember);
var getter=getterLambda.Compile();
返回getter();
}
公共静态IQueryable GroupByDateDiff(此IQueryable源、表达式键选择器){
var body=(NewExpression)keySelector.body;
foreach(body.Arguments中的变量arg){
if(arg.NodeType==ExpressionType.Call){
var callNode=(MethodCallExpression)arg;
if(callNode.Method.Name==“DateDiff”){
var dateDiffFirstArg=callNode.Arguments[0];
if(dateDiffFirstArg.NodeType==ExpressionType.Constant){
//这已经是一个常数了,所以我们很好。
}
否则{
//HACK:如果ReadOnlyCollection的内部实现发生更改,这将中断。
var listInfo=typeof(ReadOnlyCollection).GetField(“list”,System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var list=(IList)listInfo.GetValue(callNode.Arguments);
if(dateDiffFirstArg.NodeType==ExpressionType.MemberAccess){
列表[0]=表达式.常量((字符串)GetMemberValue((MemberExpression)dateDiffFirstArg));
}
否则{
抛出新ArgumentException($“{nameof(GroupByDateDiff)}无法将datePartArg参数解析为DateDiff函数。”);
}
}
}
}
}
返回source.GroupBy(keySelector);
}
请注意,这使用反射来访问私有变量,如果ReadOnlyCollection的实现发生更改,则可能会中断。但是,如果发生这种情况,解决这个问题将相当容易