Преобразовать этот SQL с левым соединением в синтаксис метода Linq

#c# #linq

#c# #linq

Вопрос:

Мне нужен эквивалент этого оператора SQL в Linq, используя синтаксис метода / fluent.

 SELECT u.[UserId], s.[UserId], d.[UserId]
FROM dbo.[Attachment] z  
INNER JOIN dbo.[Activity] a ON z.[ActivityId] = a.[ActivityId]
INNER JOIN dbo.[Case] c ON a.[CaseId] = c.[CaseId]
INNER JOIN dbo.[CaseUser] x ON c.[CaseId] = x.[CaseId]
INNER JOIN dbo.[User] u ON x.[UserId] = u.[UserId]
LEFT OUTER JOIN dbo.[User] s ON u.[SupervisorId] = s.[UserId]
LEFT OUTER JOIN dbo.[User] d ON s.[SupervisorId] = d.[UserId]
WHERE u.[UserId] = @x OR s.[UserId] = @x OR d.[UserId] = @x
  

Кроме того, я использую его в контексте, где я должен вернуть System.Linq.Выражения.Объект выражения. Например, примером существующего, более простого кода может быть:

 public override Expression<Func<Attachment, bool>> MatchingCriteria
{
  get { return a => a.Activity.Case.CaseUsers.Any(x => (x.User.Id == this.id)); }
}
  

Я поставлен в тупик левыми соединениями, использующими синтаксис метода, который вычисляется как bool.

Ответ №1:

Я думаю, что следующее должно сработать… Вы проверяете, чтобы убедиться, что связанный объект в модели не равен null, а затем выполняете сравнение, если это не так:

 return a => a.Activity.Case.CaseUsers
    .Where(cu => cu.User.Id == this.id || 
           (cu.User.Supervisor != null amp;amp; 
            cu.User.Supervisor.Id == this.Id) || 
           (cu.User.Supervisor != null amp;amp; 
            cu.User.Supervisor.Supervisor != null amp;amp; 
            cu.User.Supervisor.Supervisor.Id == this.Id));
  

Комментарии:

1. Обратите внимание, что это не соединение и, следовательно, правильный пользователь не возвращается.

2. Что ж, я заменил WHERE на ANY, поскольку я, по сути, тестирую наличие идентификатора пользователя в цепочке пользователей. Кажется, это работает, но будет тестироваться дольше. Спасибо за предупреждение.

3. Я написал этот запрос, предполагая, что он не хочет возвращать пользователя. Я почти уверен, что сгенерированный SQL должен примерно соответствовать его запросу, но для проверки вам придется использовать ToTraceString() или Profiler.

Ответ №2:

Используя следующий метод, вы можете проверить, существует ли какой-либо из требуемых идентификаторов, и выбрать элемент.

С помощью примера определения класса, подобного:

 class Foo
{
    public int FirstBarId { get; set; }

    public int SecondBarId { get; set; }
}

class Bar
{
    public int BarId { get; set; }
}
  

Вы можете запросить, например:

 var query = fooSet.Select(foo => new
                    {
                        Foo = foo,
                        Bar1 = barSet.FirstOrDefault(bar => foo.FirstBarId == bar.BarId),
                        Bar2 = barSet.FirstOrDefault(bar => foo.SecondBarId == bar.BarId)
                    })
                    .Where(x => x.Bar1 != null || x.Bar2 != null);
  

Комментарии:

1. На самом деле, рассматриваемый класс является рекурсивным: класс User { общедоступный пользовательский супервизор {get; set;}}

2. @alphadogg — Это ничего не меняет 🙂

3. Не так ли? Я имею в виду, что у вас есть два «независимых» дочерних элемента Bar для Foo, тогда как моя проблема заключается в том, как перейти на несколько уровней дочерних элементов?

4. @alphadogg — Переход на несколько уровней — это просто так. Вы можете легко расширить предоставленный мной запрос. Как вы думаете, в чем разница при замене barSet на fooSet?