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

发布于 2024-11-07 07:29:27 字数 1568 浏览 0 评论 0原文

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

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 无法识别该方法”异常。

I want to be able to somehow break up the following code:

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
       };

For example, I am looking for a way to factor out:

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

into a separate method for reusability purposes, yet I can't figure out exactly how to go about doing this. I have tried:

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();
}

Which gives me my old favorite, 'LINQ to Entities does not recognize the method' exception.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

心病无药医 2024-11-14 07:29:27
// 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
};
// 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
};
口干舌燥 2024-11-14 07:29:27

这个问题可能不会得到很多关注,所以我发布了一个相关问题,帮助我尝试找到更好的解决方案:

重构 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
           };
}

This question probably won't get a lot of action, so I am posting a related question that has helped me try to find a better solution:

refactoring LINQ IQueryable expression to remove duplicated portions of queries

Here's the code for my particular variation of a solution:

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();
    }
}

-- UPDATE 5/13/2011 --

The example above performs an inner join and will not work for instances where you want to include all employees even if EmployeeVideoAggregatesView() returns no results, so to allow for left outer joining, I had to tweak the code a bit:

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
           };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文