使用 let 变量和子查询重构 LINQ to Entities 查询
本文关键字:查询 重构 LINQ to Entities let 变量 使用 | 更新日期: 2023-09-27 17:56:02
我希望能够以某种方式分解以下代码:
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无法识别该方法"异常。
// 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
};
这个问题可能不会得到很多行动,所以我发布了一个相关的问题,帮助我尝试找到更好的解决方案:
重构 LINQ IQueryable 表达式以删除查询的重复部分
以下是我特定解决方案变体的代码:
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() 未返回任何结果,因此为了允许左外部联接,我不得不稍微调整代码:
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
};
}