是的,当然你可以扩展它。 DbFunctions 类仅包含帮助 IQueryable 提供者将表达式转换为 SQL 的代码。
IEnumerable 包含所有代码,可根据您的请求为您创建 Enumerator。 Enumerator 代表一个序列。根据请求,它会给你序列的第一个元素,一旦你有了一个元素,它就会给你下一个元素(只要有一个)。
IQueryable 的工作方式不同。通常 IQueryable 并不意味着由您的进程执行,而是由数据库、远程网站、CSV 文件控制器等执行。
这就是为什么您需要告诉一个生成 IQueryable 的对象,它必须为哪个进程创建 IQueryable。在实体框架的情况下,您通知 DbContext 使用哪个数据库。
IQueryable 对象包含一个要执行的表达式和一个提供程序。提供者知道哪个进程将执行查询。它还知道如何将表达式翻译成其他进程可以理解的格式。这通常是 SQL。
如果您查看 MSDN 描述 IQueryable 函数(如 Where、GroupBy、Select)的备注部分,您会发现这些函数中的大多数只会更改表达式。
只要你不要求 Enumerator,通常是通过要求序列的第一个元素来隐式地要求,例如在 ToList、foreach、FirstOrDefault 等中,Provider 就没有任何关系。
但是,一旦您请求 Enumerator,Expression 将由 Provider 翻译,Provider 将使用翻译从其他进程中查询数据并创建一个 Enumerator 对象,该对象可以为您提供序列的第一个元素,并且下一个。
提供者将表达式转换为 SQL 时使用 DbFunctions。如果您使用 DbFunctions 创建 Queryable 并在调试器中查看创建的表达式,您仍然会找到使用的 DbFunctions。
DbFunction 仅将输入转换为 SQL。如果不执行查询本身。翻译在本地内存中完成。
了解这一点后,您可以使用任何函数,只要它只将表达式更改为您的提供者可以理解的新表达式即可。
这意味着您不能使用任何自己的函数或类。甚至有几个 LINQ 函数你不能用
见supported and non-supported LINQ methods
但是,如果您的扩展函数输入一个 IQueryable 并输出一个 IQueryable,那么您的扩展函数只会更改表达式。只要您使用受支持的 LINQ 方法填充表达式就可以了
因此,如果您想使用返回仅包含到期发票的 IQueryable 的函数来扩展 IQueryable:
public static IQueryable<Invoice> WhereDueToday(this IQueryable<Invoice> invoices)
{ // returns all invoices that must be paid today
return invoices
.Where(invoice => DbFunctions.TruncateTime(invoice.DueDate) == DateTime.Today);
}
用法:
IQueryable<Invoice> invoices = dbContext.Invoices
.Where(invoice => ..);
IQueryable<Invoice> invoicesDueToDay = invoices
.WhereDueToday();