错误信息的原因
您应该了解IEnumerable 和IQueryable 之间的区别。
实现IEnumerable 的类的对象包含要枚举它所代表的项目序列的所有内容。您可以请求序列中的第一项,一旦获得了一项,您就可以请求下一项,直到没有更多项为止。
另一方面,实现IQueryable 的类的对象拥有一切要求另一个进程提供数据以创建 IEnumerable 序列。为此,它拥有一个Expression 和一个Provider。
Expression 是在开始枚举IQueryable 后必须创建哪种 IEnumerable 的通用表示。
Provider 知道谁必须执行查询,它知道如何将Expression 转换为执行程序可以理解的格式,例如 SQL。
有两种 LINQ 语句。那些使用延迟执行的,以及那些不使用的。可以识别延迟函数,因为它们返回 IQueryable<TResult>(或 IEnumerable)。例如Where、Select、GroupBy 等。
非延迟函数返回TResult:ToList、ToDictionary、FirstOrDefault、Max。
只要连接延迟的 LINQ 函数,就不会执行查询,只会更改 Expression。一旦开始枚举,无论是显式使用GetEnumerator 和MoveNext,还是隐式使用foreach、ToList、Max 等,Expression 都会发送到Provider,Provider 会将其转换为SQL 并执行查询。结果表示为IEnumerable,在其上执行GetEnumerator。
这与我的问题有什么关系?
因为Expression 必须被翻译成SQL,它不能容纳你发明的任何东西。毕竟,SQL 不知道你的函数。事实上,IQueryable 中不能使用很多标准函数。见Supported and unsupported LINQ functions
唉,你忘了给我们archive 类定义,但我认为它不是 POCO:它包含的功能和属性不仅仅是获取/设置。我认为IssuingDate 不只是获取/设置。
对于 IQueryables,您应该保持类简单:在查询期间仅使用 {get; set;},仅此而已。在将 IQueryable 物化为要在本地进程中执行的 IEnumerable 后,可以调用其他函数
回到你的问题
所以你有一个数据库,其中有一个表Archive,其中至少有IssuingDate 和InsertedBy 列。似乎InsertedBy 只是一个字符串。它可能是用户表的外键。这不会对答案产生太大影响。
按照entity framework code first conventions 这会导致以下类
class Archive
{
public int Id {get; set;}
public DateTime IssuingDate {get; set;}
public string InsertedBy {get; set;}
...
}
public class MyDbContext : DbContext
{
public DbSet<Archive> Archives {get; set;}
}
顺便说一句,您经常偏离 Microsoft 关于命名标识符的标准,尤其是复数和驼峰式大小写,是否有正当的理由?
不管怎样,你的要求
我的表单上有两个日期时间选择器。我想要一个函数,它将返回这两个日期之间特定表(即特定列的值)中的所有日期时间。
您的代码似乎做得更多,但让我们先编写一个满足您要求的扩展函数。我会把它写成你归档类的扩展方法。这将使您的存档类保持简单(仅{get; set;}),但它为类添加了功能。将其编写为扩展函数还使您能够像使用任何其他 LINQ 函数一样使用这些函数。见Extension methods demystified
public static IQueryable<Archive> BetweenDates(this IQueryable<Archive> archives,
DateTime startDate,
DateTime endDate)
{
return archives.Where(archive => startDate <= archive.IssuingDate
&& archive.IssuingDate <= endDate);
}
如果我查看您的代码,您不会在日期之间选择档案。你用一个用户名做一些事情,排序,选择不同的…有点奇怪,你先订购所有的百万档案,然后决定只保留属于用户名的十个档案,如果你有几个相同的发布日期您决定删除重复项。在开始订购之前先限制发行日期的数量不是更有效吗?
public static IQueryable<archive> ToIssuingDatesOfUser(this IQueryable<archive> archives,
string userName)
{
// first limit the number of archives, depdning on userName,
// then select the IssuingDate, remove duplicates, and finally Order
var archivesOfUser = (userName == null) ? archives :
archives.Where(archive => archive.InsertedBy == userName);
return archivesOfUser.Select(archive => archive.IssuingDate)
.Distinct()
.OrderBy(issuingDate => issuingDate);
}
注意:到目前为止,我只创建了 IQueryables。所以只更改了Expression,这是相当有效的。数据库尚未通信。
使用示例:
要求:给定一个用户名、一个开始日期和一个结束日期,给我这个用户发布的所有档案的唯一发布日期,按升序排列
public ICollection<string> GetIssuingDatesOfUserBetweenDates(string userName,
DateTime startDate,
DateTime endDate)
{
using (var dbContext = new MyDbContext(...))
{
return dbContext.Archives
.BetweenDates(startDate, endDate)
.ToIssuingDatesOfUser(userName)
.ToList();
}
}