LINQ Self Ссылка на таблицу SELECT Query - PullRequest
0 голосов
/ 14 октября 2019

У меня есть собственная таблица сотрудников, то есть сотрудники с первичным ключом «EmployeeID» и внешним ключом «SupervisorID» для родителя (супервизора). Менеджеры находятся на вершине иерархии, поэтому их SupervisorID равен нулю. Есть также столбец даты (CreatedOn), чтобы фильтровать по определенным датам, но только у менеджеров есть дата, а не их сотрудники. Я пытаюсь получить все результаты от конкретной даты, менеджеров и их сотрудников, но я просто не могу запустить ее. Мне нужно добиться этого с помощью LINQ, но я подумал, что сначала смогу выполнить SQL-запрос, чтобы посмотреть, смогу ли я каким-то образом перенести это в запрос LINQ (не имеет значения, какой синтаксис). Но это было очень иллюзорно, когда я понял, что это так же сложно, как в LINQ ... Я думал, что это может быть достигнуто с помощью CTE, но я не могу заставить себя работать. Либо я просто получаю менеджеров, либо ничего ... Вот пример таблицы:

------------------------------------------------------
|EmployeeID|Name    |SupervisorID|CreatedOn          |
|1         |Sanders |NULL        |2019-01-01 16:20:33|
|2         |Flanders|1           |NULL               |
|3         |Andrews |1           |NULL               |
|4         |Ranger  |NULL        |2019-03-20 18:04:56|
|5         |Hammer  |4           |NULL               |
|6         |Enderson|4           |NULL               |
|7         |Larsson |NULL        |2019-10-03 04:55:16|
|8         |Simpson |8           |NULL               |
------------------------------------------------------

Как видите, у Сандерса (2019-01-01 16:20:33) 2 сотрудника (Фландрия, Эндрюс), Ranger 2019-03-20 18:04:56 имеет 2 сотрудников (Хаммер, Эндерсон) и Ларссон (2019-10-03 04:55:16) имеет 1 сотрудника (Симпсон)

Я неполучить где-нибудь с LINQ. Если я отфильтрую дату, у меня больше не будет возможности отфильтровать начальный набор данных для сотрудников.

// This filters by date, but then I can't get the employees without date anymore
employees.Where(e => e.CreatedOn >= '2019-01-01' && e.CreatedOn < '2019-01-02')

// This filters all employees with a parent, but then I can't get the managers anymore and can't filter against a date anymore either
employees.Where(e => e.SupervisorID != null)

Я попробовал объединение, но это тоже не сработало:

from employees m in employees
join e in employees
on m.EmployeeId equals e.SupervisorID
where m.CreatedOn >= '2019-01-01' && m.CreatedOn < '2019-01-02'
select m

Ожидаемым набором результатов для соответствующего фильтра будут Сандерс и его сотрудники, поскольку запись Сандерса была создана 1 января и поскольку его сотрудники работают под ним:

------------------------------------------------------
|EmployeeID|Name    |SupervisorID|CreatedOn          |
|1         |Sanders |NULL        |2019-01-01 16:20:33|
|2         |Flanders|1           |NULL               |
|3         |Andrews |1           |NULL               |
------------------------------------------------------

У кого-нибудь есть идеи, как мне этого добиться? Я не смогу изменить дизайн базы данных, поскольку могу только читать, а другое приложение отправляет данные, которые я не могу изменить.

Ответы [ 4 ]

1 голос
/ 14 октября 2019

Давайте посмотрим на руководителей и сотрудников по отдельности, а затем объединим их позже.

Руководители

Для руководителя логика довольно проста, вы фильтруетена дату создания.

var fromDate = new DateTime(2019,1,1);
var untilDate = new DateTime(2019,1,2);

var supervisors = employees
                         .Where(e => e.CreatedOn >= fromDate 
                                  && e.CreatedOn < untilDate )
                         .ToList();

Примечание: в вашем наборе данных только супервизоры имеют дату создания. Если у сотрудников также может быть дата создания, вам необходимо явно отфильтровать их:

var supervisors = employees
                         .Where(e => e.CreatedOn >= fromDate 
                                  && e.CreatedOn < untilDate 
                                  && e.SupervisorID == null)
                         .ToList();

Сотрудники

Логика для сотрудников несколько сложнее, новы можете полагаться на навигационные свойства EF, чтобы обрабатывать объединения для вас.

Короче говоря, вы хотите, чтобы сотрудники, руководитель которых имел дату создания в выбранном диапазоне.

var fromDate = new DateTime(2019,1,1);
var untilDate = new DateTime(2019,1,2);

var subordinates = employees
                         .Where(e => e.SupervisorID != null
                                  && e.Supervisor.CreatedOn >= fromDate 
                                  && e.Supervisor.CreatedOn < untilDate )
                         .ToList();

Объединение двух

Объединить их довольно просто, эффективно выполняется supervisorFilter OR subordinateFilter:

var fromDate = new DateTime(2019,1,1);
var untilDate = new DateTime(2019,1,2);

var supervisorsAndSubordinates = employees
                         .Where(e => 
                                  // Supervisor filter
                                  (
                                       e.CreatedOn >= fromDate 
                                    && e.CreatedOn < untilDate
                                  )
                                  ||
                                  // Subordinate filter
                                  (
                                       e.SupervisorID != null
                                    && e.Supervisor.CreatedOn >= fromDate 
                                    && e.Supervisor.CreatedOn < untilDate
                                  ))
                         .ToList();

Это даст вам ожидаемый набор результатов. Обратите внимание, что решение становится значительно сложнее, когда эта структура является рекурсивной (т. Е. Несколько уровней «супервизор супервизора»), но в настоящее время это не так в вашем примере.


0 голосов
/ 14 октября 2019

Вам нужно левое внешнее соединение

Т.е. что-то вроде

 var mylist = (from employee in employees
              join r in employees on employee.SupervisorId equals r.EmployeeId  into results                  
              from supervisor in results.DefaultIfEmpty()

               let createdDate = supervisor != null ? supervisor.CreatedOn : employee.CreatedOn                  
               where  createdDate >= new DateTime(2019,1,1) && createdDate < new DateTime(2019,1,2) 
               select employee);

Я предполагаю, что существует только два уровня (то есть никто не может бытьруководитель и есть руководитель).

0 голосов
/ 14 октября 2019

используя объединение, вы можете иметь как менеджеров, так и их подчиненных:

var list = employees.Where(m=> m.CreatedOn >= new DateTime(2019,1,1) && m.CreatedOn < new DateTime(2019,1,2)).Join(employees,sc=> sc.EmployeeId, soc=> soc.SupervisorId, (sc,soc)=> new {left= sc, right=soc}  ).ToList();

Я создал скрипку, проверьте по ссылке ниже:

C # fiddle компилятора

примечание: я присоединился к сотрудникам сам по себе, рассматривая фильтр даты для левого списка и присоединяясь к supervisorId в правом списке. Это показывает менеджер, и его / ее соответствующий сотрудник

0 голосов
/ 14 октября 2019

Все методы Linq оцениваются как ленивые, для немедленного получения результата вы должны использовать метод терминала.

Методы терминала, такие как ToList, ToArray, ForEach ...

employees.Where(e => e.SupervisorID != null).ToList()
...