Фильтрация по максимуму и группировка по идентификатору с присоединениями к другим объектам в LINQ to Entity Framework (C #) - PullRequest
0 голосов
/ 11 ноября 2010

Следующий фрагмент работает для того, что мне нужно.Хотя я верю, что должна быть лучшая практика?Более оптимальный способ сделать этот запрос?

Необходимо получить список объектов сотрудников, которые являются прямыми отчетами для сотрудников / мг х.Прямые отчеты заносятся в таблицу истории, в которой есть несколько записей для каждого сотрудника, поэтому из этой таблицы следует возвращать только одну (самую последнюю) запись для каждого прямого отчета (сотрудника), а затем использовать таблицу Employee для полученияобъект сотрудника, где идентификатор сотрудника равен идентификатору сотрудника из каждой записи истории в этом отфильтрованном наборе результатов.Я могу получить обе половинки с двумя отдельными запросами LINQ to EF.

Проблема возникает при попытке присоединиться к объекту employeeHistory из первого набора результатов.Согласно MSDN: ссылка на нескалярные замыкания не поддерживается [Ссылка на нескалярное замыкание, такое как объект, в запросе не поддерживается.Когда такой запрос выполняется, генерируется исключение NotSupportedException с сообщением, в котором говорится: «Невозможно создать постоянное значение типа« Тип закрытия ». В этом контексте поддерживаются только примитивные типы (такие как Int32, String и Guid»).. "]

Итак, я запускаю два запроса и создаю первый список типа int, а не сложный объект.Это работает, но кажется надуманным.Любые предложения относительно лучшего способа (я хотел бы сделать один запрос).

private List<BO.Employee> ListDirectReports(int mgrId)
{
    IQueryable<BO.Employee> directRpts;
    using(var ctx = new Entities())
    {
        //to get a list of direct rpts we perform two separate queries. linq to ef with linq to objects
        //first one gets a list of emp ids for a direct mgr emp id from the history table
        //this first qry uses grouping and a filter by empid and a filter by max(date)
        //the second qry joins to the resultset from the first and goes to the employee table 
        //to get whole employee objects for everyone in the int emp id list from qry #1

        //qry #1: just a list of integers (emp ids for those reporting to emp id of mgrId)
        IEnumerable<int> directRptIDList = 
            from employeeHistory in ctx.EmployeeHistory
            .Where(h => h.DirectManagerEmployeeID == mgrId).ToList()
                group employeeHistory by employeeHistory.EmployeeID into grp 
                    let maxDt = grp.Max(g => g.DateLastUpdated) from history in grp
                    where history.DateLastUpdated == maxDt
                    select history.EmployeeID;

        //qry #2: a list of Employee objects from the Employee entity. filtered by results from qry #1:
        directRpts = from emp in ctx.Employee
            join directRptHist in directRptIDList.ToList()
            on emp.EmployeeID equals directRptHist
            select emp;
    }
    return directRpts.ToList();
}

Спасибо.

Ответы [ 3 ]

2 голосов
/ 11 ноября 2010

2 вещи, которые я могу придумать, чтобы улучшить ваши запросы:

ToList без защиты. Вызов этого в ваших Queryable коллекциях вызывает много дополнительных поездок в БД. Я также считаю, что этот вызов, наряду с явным объявлением IEnumerable<int>, вызывал ошибку закрытия.

Используйте отношение между EmployeeHistory и Employee в вашем ObjectContex, чтобы присоединиться к запросам. Это позволит платформе производить более эффективную SQL. И когда directRpts оценивается по вашему вызову ToList, он должен совершить только 1 поездку в БД.

Дайте мне знать, если это поможет.

private List<BO.Employee> ListDirectReports(int mgrId)
{
    using(var ctx = new Entities())
    {
        var directRptIDList = 
            from employeeHistory in ctx.EmployeeHistory
                                       .Where(h => h.DirectManagerEmployeeID == mgrId)
            group employeeHistory by employeeHistory.EmployeeID into grp 
            let maxDt = grp.Max(g => g.DateLastUpdated) from history in grp
            where history.DateLastUpdated == maxDt
            select history;

        var directRpts = 
            from emp in ctx.Employee
            join directRptHist in directRptIDList
            on emp equals directRptHist.Employee
            select emp;
    }
    return directRpts.ToList();
}
0 голосов
/ 11 ноября 2010

Спасибо, Соракс. Код, который я разместил, не содержит ошибок и дает мне нужные результаты, но, как вы указали, объединение двух запросов с ошибками при включении метода ToList (). Используя ваш совет, я успешно слил (протестировал) и опубликовал улучшенный метод одиночного запроса ниже. StriplingWarrior Я тоже попробовал твой, может быть, смог бы еще больше помассировать. При первом запросе время ожидания функции оценки истекло, поэтому сейчас я буду придерживаться предложения Sorax. Я ценю помощь и вернусь к этому.

    private static List<BO.Employee> ListDirectReports(int mgrId)
    {
        IQueryable<BO.Employee> directRpts;
        using(var ctx = new Entities())
        {
            directRpts = 
                from emp in ctx.Employee
                join directRptHist in 
                (from employeeHistory in ctx.EmployeeHistory
                    .Where(h => h.DirectManagerEmployeeID == mgrId)
                group employeeHistory by employeeHistory.EmployeeID into grp 
                let maxDt = grp.Max(g => g.DateLastUpdated) from history in grp
                where history.DateLastUpdated == maxDt
                select history)
                on emp equals directRptHist.Employee
                select emp;
        }
        return directRpts.ToList();

        //IQueryable<BO.Employee> employees;
        //using(var ctx = new Entities())
        //{

        //        //function evaluation times out on this qry:

        //    // First make sure we're only looking at the current employee information
        //    IQueryable<BO.EmployeeHistory> currentEntries = 
        //        from eh in ctx.EmployeeHistory
        //        group eh by eh.EmployeeID into grp 
        //        select grp.OrderBy(eh => eh.DateLastUpdated).FirstOrDefault();

        //    // Now filter by the manager's ID
        //    var dirRpts = currentEntries
        //        .Where(eh => eh.DirectManagerEmployeeID == mgrId);

        //    // This would be ideal, assuming your entity associations are set up right
        //    employees = dirRpts.Select(eh => eh.Employee).Distinct();

        //    //// If the above won't work, this is the next-best thing
        //    //var employees2 = ctx.Employee.Where(
        //    //                    emp => directRpts.Any(
        //    //                        eh => eh.EmployeeId == emp.EmployeeId));
        //}
        //return employees.ToList();
    }
0 голосов
/ 11 ноября 2010

Здесь есть ряд проблем, не последним из которых является то, что при выполнении Where до вы получаете самый последний элемент истории, вы получаете записи, которые больше не действительны.Вот как я это сделаю:

private List<BO.Employee> ListDirectReports(int mgrId)
{
    using(var ctx = new Entities())
    {
        // First make sure we're only looking at the current employee information
        var currentEntries = 
            from eh in ctx.EmployeeHistory
            group employeeHistory by employeeHistory.EmployeeID into grp 
            select grp.OrderBy(eh => eh.DateLastUpdated).FirstOrDefault();
        // Now filter by the manager's ID
        var directRpts = currentEntries
            .Where(eh => eh.DirectManagerEmployeeID == mgrId);

        // This would be ideal, assuming your entity associations are set up right
        var employees = directRpts.Select(eh => eh.Employee).Distinct();

        // If the above won't work, this is the next-best thing
        var employees2 = ctx.Employee.Where(
                            emp => directRpts.Any(
                                eh => eh.EmployeeId == emp.EmployeeId));

        return employees.ToList();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...