是否可以将 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 方法中,LocationDateTime 需要使用 j.Returnj.Departure,具体取决于方向。因此,我试图将其移至扩展方法。 检查最后的编辑和第二个语法。我手头没有编译器,但类似的东西是完成此任务的更简单方法。 不知道投反对票。你在方法语法查询中的解决方案.Select(job =&gt; new deptOrReturn = direction == "Pick up" ? job.Departure : job.Return, job) .Select(j =&gt; 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.Namelocation.MeetingLocation.Name 无效 Invoke() 返回一个 System.Linq.Expressions.Expression 而不是来自 func 的对象 不是,这是 Linqkit 库中Expression 类的扩展方法,你安装了吗? 无法检查此 atm 的正确性,我忘记了此 .sln 仍在 EF 4.3 上 :( 升级后必须查看。 我对此投了反对票,因为对于这样一个简单的 linq-ready 任务,您建议了一个表达式扩展方法,需要安装一个动态 linq 包。

以上是关于是否可以将 Linq 中的 lambda 参数化为实体选择的主要内容,如果未能解决你的问题,请参考以下文章

LINQ 以 Lambda 形式选择不同计数

C#将字典的LINQ查询重构为单个lambda表达式[关闭]

将 lambda 表达式转换为 ORM 中的 SQL?

将 Linq 结果直接序列化为 JSON

如何使用lambda表达式和LINQ将数据插入数据库表?

lambda,linq