是否可以在 Linq to Entities 中参数化 lambda Select

Is it possible to parameterise a lambda in Linq to Entities Select

我有 2 个 entity framework 查询,除了 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,
    });

我尝试创建一个扩展方法来共享 select,它可以在没有 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,                    
            });
    }

我看到他的错误信息:

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

不要使用 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:

ToExpandableDLINQ Table 对象周围创建一个薄包装。多亏了这个包装器,您可以使用名为 Invoke 的第二种方法,它扩展了 Expression class 来调用 lambda 表达式,同时仍然可以将查询转换为 T-SQL。这是有效的,因为当转换为表达式树时,包装器用调用的 lambda 表达式的表达式树替换所有出现的 Invoke 方法,并将这些表达式传递给能够将扩展查询转换为 T-[=27 的 DLINQ =].

将此语法与let语句结合使用,一举实现合成。您需要一个基础 class,或者更好的接口,用于您的 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,
    };