考虑我的Event
班级,并将我DateTime
作为UTC日期存储在数据库中.我只想根据特定时区的当前日期返回过滤范围 - 这很简单吗?
这很好用:
IQueryabletest1 = this.GetSortedEvents().Where(e => e.FinishDateTime.Date >= DateTime.UtcNow.Date);
这也很好:
IQueryabletest2 = this.GetSortedEvents().Where(e => e.FinishDateTime.AddHours(3).Date >= DateTime.UtcNow.AddHours(3).Date);
..并且还符合我的时区要求.
所以在这里我想我可以将这个特定的转换移到这个扩展方法:
public static DateTime RiyadhTimeFromUtc(this DateTime utcTime) { return utcTime.AddHours(3); }
这不起作用:
IQueryabletest3 = this.GetSortedEvents().Where(e => e.FinishDateTime.RiyadhTimeFromUtc().Date >= DateTime.UtcNow.RiyadhTimeFromUtc().Date);
..我得到这个NotSupportedException:方法'System.DateTime RiyadhTimeFromUtc(System.DateTime)'没有支持的SQL转换.
这显然是垃圾,因为当相同的代码不在扩展方法中时,编译器很乐意将其转换为SQL.
在使用某些类型和最近的DateTime之前,我遇到了"没有支持的SQL转换"问题.但我上面的测试和此链接证明了SQL转换中应该支持AddHours方法.
如果有人能告诉我这里我做错了什么(或者解决这个问题的方法不同),我真的很感激.
您必须根据表达式树来考虑它,这是Linq-to-SQL解析您的查询以将其转换为SQL的方式.
当检查树就会看到一个DateTime
对象,然后检查调用它的方法是与支援的(一个Add
,AddHours
等),所以当你直接使用方法,它工作正常.
当你使用其他一些扩展方法时,它不能去查看该方法以查看它的作用,因为关于该方法的主体的信息不在表达式树中,它隐藏在IL中.因此,是否支持扩展方法的内容并不重要,因为Linq-to-SQL无法确定内容是什么.
创建方法的关键是封装和信息隐藏,这通常适用于应用程序开发,但不幸的是,它隐藏了Linq-to-SQL中需要能够查看信息的信息.
回答编辑过的问题 - 你如何解决这个问题?如果要将日期计算保留在Linq表达式中,那么唯一可以保持查询效率的方法就是不使用扩展方法,而只是AddHours(3)
直接在DateTime
对象上使用.
这是Linq不幸的局限之一.像许多事情一样,它是一个有点漏洞的抽象,虽然在一系列源代码上提供了通用语法,但对源代码可以/将支持的操作有不同的限制和限制(例如,这在Linq-to中完全可以正常工作) - 对象,因为它不需要转换表达式树来执行它).