使用let变量和子查询重构LINQ to Entities查询

时间:2011-05-11 21:10:35

标签: c# .net linq

我希望能够以某种方式分解以下代码:

return from e in _context.Employees
       let HasWatchedAllVideos = 
       (
           from ev in _context.EmployeeVideos
           where ev.EmployeeId == e.Id && ev.EndTime.HasValue
           select ev.Id
       ).Count() == _context.Videos.Count()
       let EndTime = HasWatchedAllVideos ?
       (
           from ev in _context.EmployeeVideos
           where ev.EmployeeId == e.Id
           select ev.EndTime
       ).Max() : null
       let StartTime =
       (
           from ev in _context.EmployeeVideos
           where ev.EmployeeId == e.Id
           select ev.StartTime
       ).Min()
       select new EmployeeListItem
       {
           Id = e.Id,
           FirstName = e.FirstName,
           LastName = e.LastName,
           Company = e.Company,
           HasWatchedAllVideos = HasWatchedAllVideos,
           StartTime = StartTime,
           EndTime = EndTime
       };

例如,我正在寻找一种分解出来的方法:

let HasWatchedAllVideos = 
(
    from ev in _context.EmployeeVideos
    where ev.EmployeeId == e.Id && ev.EndTime.HasValue
    select ev.Id
).Count() == _context.Videos.Count()

为了可重用性而单独使用一种方法,但我无法弄清楚如何去做这件事。我试过了:

private bool HasWatchedAllVideos(int employeeId)
{
    return (from ev in _context.EmployeeVideos
            where ev.EmployeeId == employeeId && ev.EndTime.HasValue
            select ev.Id
            ).Count() == _context.Videos.Count();
}

这给了我最喜欢的,'LINQ to Entities无法识别方法'例外。

2 个答案:

答案 0 :(得分:0)

这个问题可能不会有很多动作,所以我发布的相关问题帮助我找到了更好的解决方案:

refactoring LINQ IQueryable expression to remove duplicated portions of queries

以下是我解决方案特定变体的代码:

public class AdaTrainingService : ADATraining.Web.Models.IAdaTrainingService, IDisposable
{
    private ADATrainingEntities _context = new ADATrainingEntities();

    public IQueryable<EmployeeListItem> GetEmployeeListing()
    {
        return from e in _context.Employees
               join evsws in EmployeeVideoAggregatesView() on e.Id equals evsws.EmployeeId
               select new EmployeeListItem
               {
                   Id = e.Id,
                   FirstName = e.FirstName,
                   LastName = e.LastName,
                   Company = e.Company,
                   HasWatchedAllVideos = evsws.HasWatchedAllVideos,
                   StartTime = evsws.StartTime,
                   EndTime = evsws.EndTime
               };
    }

    private class EmployeeVideoSeriesWatchingStats
    {
        public int EmployeeId { get; set; }
        public DateTime? StartTime { get; set; }
        public DateTime? EndTime { get; set; }
        public bool HasWatchedAllVideos { get; set; }
    }

    private IQueryable<EmployeeVideoSeriesWatchingStats> EmployeeVideoAggregatesView()
    {
        return from ev in _context.EmployeeVideos
               group ev by ev.EmployeeId into myGroup
               select new EmployeeVideoSeriesWatchingStats
               {
                   EmployeeId = myGroup.Key,
                   StartTime = myGroup.Min( x => x.StartTime),
                   EndTime = myGroup.Max( x => x.EndTime),
                   HasWatchedAllVideos = myGroup.Count() ==  _context.Videos.Count()
               };
    }   

    public void Dispose()
    {
        _context.Dispose();
    }
}

- 更新5/13/2011 -

上面的示例执行内部联接,并且不适用于您希望包含所有员工的实例,即使EmployeeVideoAggregatesView()没有返回任何结果,因此为了允许左外部连接,我不得不稍微调整一下代码:< / p>

public IQueryable<EmployeeDetails> GetEmployeeListing()
{
    return from e in _context.Employees
           join evsws in EmployeeVideoAggregatesView() on e.Id equals evsws.EmployeeId into myJoin
           from mj in myJoin.DefaultIfEmpty()
           select new EmployeeDetails
           {
               Id = e.Id,
               FirstName = e.FirstName,
               LastName = e.LastName,
               Company = e.Company,
               BadgeNumber = e.BadgeNumber,
               Title = e.Title,
               HasWatchedAllVideos = (mj.HasWatchedAllVideos == null) ? false : mj.HasWatchedAllVideos,
               StartTime = mj.StartTime,
               EndTime = mj.EndTime
           };
}

答案 1 :(得分:0)

// don't count every time
var totalCount = _context.Videos.Count();

from e in _context.Employees
let HasWatchedAllVideos =
    totalCount ==
    _context.EmployeeVideos.Count(ev => ev.EmployeeId == e.Id && ev.EndTime.HasValue)

// count just once per employee
let employeeVideos =  _context.EmployeeVideos.Count(ev => ev.EmployeeId == e.Id)

let EndTime = HasWatchedAllVideos ? employeeVideos.Max() : null
let StartTime =  HasWatchedAllVideos ? employeeVideos.Min() : null

select new EmployeeListItem
{
    Id = e.Id,
    FirstName = e.FirstName,
    LastName = e.LastName,
    Company = e.Company,
    HasWatchedAllVideos = HasWatchedAllVideos,
    StartTime = StartTime,
    EndTime = EndTime
};