2 Stimmen

Refactoring von LINQ to Entities-Abfragen mit let-Variablen und Unterabfragen

Ich möchte in der Lage sein, den folgenden Code irgendwie aufzubrechen:

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

Ich suche zum Beispiel nach einer Möglichkeit, einen Faktor zu berechnen:

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

in eine separate Methode für die Wiederverwendbarkeit Zwecke, aber ich kann nicht herausfinden, genau, wie man über dies zu tun gehen. Ich habe es versucht:

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

Das gibt mir meine alte Lieblings, 'LINQ to Entities erkennt nicht die Methode' Ausnahme.

0voto

Merritt Punkte 2284

Diese Frage wird wahrscheinlich nicht oft gestellt werden, deshalb stelle ich eine verwandte Frage, die mir geholfen hat, eine bessere Lösung zu finden:

Refaktorierung von LINQ IQueryable-Ausdrücken, um doppelte Teile von Abfragen zu entfernen

Hier ist der Code für meine spezielle Variante einer Lösung:

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

Das obige Beispiel führt eine innere Verknüpfung durch und funktioniert nicht für Fälle, in denen Sie alle Mitarbeiter einbeziehen möchten, selbst wenn EmployeeVideoAggregatesView() keine Ergebnisse zurückgibt. Um also eine linke äußere Verknüpfung zu ermöglichen, musste ich den Code ein wenig ändern:

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

0voto

abatishchev Punkte 94886
// 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
};

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X