是否可以将 Linq 中的 lambda 参数化为实体选择
Posted
技术标签:
【中文标题】是否可以将 Linq 中的 lambda 参数化为实体选择【英文标题】:Is it possible to parameterise a lambda in Linq to Entities Select 【发布时间】:2016-10-11 12:13:30 【问题描述】:我有 2 个实体框架查询,除了 2 个属性的 lambda 之外几乎相同; Select()
方法中的位置和日期时间。
var departQuery = _dataContext
.Job
.Where(j => j.Departure.DateTime >= startDate && j.Departure.DateTime <= endDate)
.Select(j => new DispatchDashboardItem()
JobId = j.Id,
Direction = "PickUp",
CustomerName = j.Driver.Name,
Vehicle = j.Vehicle,
Location = j.Departure.MeetingLocation.Name,
DateTime = j.Departure.DateTime,
);
var returnQuery = _dataContext
.Job
.Where(j => j.Return.DateTime >= startDate && j.Return.DateTime <= endDate)
.Select(j => new DispatchDashboardItem()
JobId = j.Id,
Direction = "DropOff",
CustomerName = j.Driver.Name,
Vehicle = j.Vehicle,
Location = j.Return.MeetingLocation.Name,
DateTime = j.Return.DateTime,
);
我已经尝试创建一个扩展方法来共享选择,它可以在没有 func 参数的情况下工作,但是会引发异常,然后我使用位置参数:
public static IQueryable<DashboardItem> SelectDashboardItem(this IQueryable<Job> query,
string direction,
Func<Job, MeetingDetail> location)
return query
.Select(j => new DashboardItem()
JobId = j.Id,
Direction = direction,
CustomerName = j.Driver.Name,
Vehicle = j.Vehicle,
// This works without using the func
Location = location(j).MeetingLocation.Name,
DateTime = location(j).DateTime,
);
我看到他的错误信息:
LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”。
【问题讨论】:
【参考方案1】:将此语法与 let 语句一起使用可以一举实现组合。您需要一个基类,或者更好的接口,以实现 Departure 和 Return 实体之间的通用性。
var query = from job in _dataContext.Job
let departureOrReturn = (direction == "PickUp" ? job.Departure : job.Return) as BaseReturnOrDeparture
where (departureOrReturn.DateTime >= startDate && departureOrReturn.DateTime <= endDate)
select new DispatchDashboardItem
JobId = job.Id,
Direction = direction,
CustomerName = job.Driver.Name,
Vehicle = job.Vehicle,
Location = deptartureOrReturn.MeetingLocation.Name,
DateTime = deptartureOrReturn.DateTime,
;
【讨论】:
抱歉,我的冗长问题可能引起了混淆,在 .Select 方法中,Location
和 DateTime
需要使用 j.Return
或 j.Departure
,具体取决于方向。因此,我试图将其移至扩展方法。
检查最后的编辑和第二个语法。我手头没有编译器,但类似的东西是完成此任务的更简单方法。
不知道投反对票。你在方法语法查询中的解决方案.Select(job => new deptOrReturn = direction == "Pick up" ? job.Departure : job.Return, job) .Select(j => new DispatchDashboardItem() JobId = j.job.Id, Location = j.deptOrReturn.MeetingLocation.Name );
对不起,不在编译器前面。你是说我应该解决一个语法问题吗?
不只是发布了使用method syntax
的等效解决方案,因为这就是我正在使用的。这就是let
无论如何解析的内容。【参考方案2】:
不要使用Func
作为参数,而是使用Expression
。传递您用作参数的相同lambda 表达式,它将隐式转换。现在要实现您所需要的,您需要使用 LinqKit 库:
public static IQueryable<DashboardItem> SelectDashboardItem(this IQueryable<Job> query,
string direction,
Expression<Func<Job, MeetingDetail>> location)
return query.AsExpandable()
.Select(j => new DashboardItem()
JobId = j.Id,
Direction = direction,
CustomerName = j.Driver.Name,
Vehicle = j.Vehicle,
// This works without using the func
Location = location.Invoke(j).MeetingLocation.Name,
DateTime = location.Invoke(j).DateTime,
);
Explanation:
ToExpandable
在DLINQ 表对象周围创建一个瘦包装器。多亏了这个包装器,您可以使用名为Invoke
的第二种方法来扩展Expression
类来调用lambda 表达式,同时仍然可以将查询转换为T-SQL。这是因为在转换为表达式树时,包装器将所有出现的 Invoke
方法替换为调用的 lambda 表达式的表达式树,并将这些表达式传递给能够将扩展查询转换为 T-SQL 的 LINQ。
【讨论】:
那么我该如何使用这个参数呢?location(j).MeetingLocation.Name
或 location.MeetingLocation.Name
无效
Invoke() 返回一个 System.Linq.Expressions.Expression 而不是来自 func 的对象
不是,这是 Linqkit 库中Expression
类的扩展方法,你安装了吗?
无法检查此 atm 的正确性,我忘记了此 .sln 仍在 EF 4.3 上 :( 升级后必须查看。
我对此投了反对票,因为对于这样一个简单的 linq-ready 任务,您建议了一个表达式扩展方法,需要安装一个动态 linq 包。以上是关于是否可以将 Linq 中的 lambda 参数化为实体选择的主要内容,如果未能解决你的问题,请参考以下文章