Условное соединение в LINQ

#c# #linq #join

#c# #linq #Присоединиться

Вопрос:

Итак, у меня небольшая проблема с linq. Я хочу, чтобы соединение было ВНЕШНИМ СОЕДИНЕНИЕМ или ВНУТРЕННИМ СОЕДИНЕНИЕМ, в зависимости от того, фильтруются ли значения в таблице

ВНЕШНЕЕ СОЕДИНЕНИЕ:

 var query = (from tblA in dc.tblA
             join tblB in GetMyTable() on tblA.Ref equals tblB.RefA into joinedTblB
             from tblB in joinedTblB.DefaultIfEmpty()
             select tblA);
  

ВНУТРЕННЕЕ СОЕДИНЕНИЕ:

 var query = (from tblA in dc.tblA
             join tblB in GetMyTable() on tblA.Ref equals tblB.RefA into joinedTblB
             from tblB in joinedTblB
             select tblA);
  

Я хочу объединить это в одном запросе и, проверив некоторое условие, выполнить либо ВНЕШНЕЕ СОЕДИНЕНИЕ, либо ВНУТРЕННЕЕ СОЕДИНЕНИЕ, что-то вроде этого:

 var query = (from tblA in dc.tblA
             join tblB in GetMyTable() on tblA.Ref equals tblB.RefA into joinedTblB
             from tblNEWB in ((checkCondition==false) ? joinedTblB.DefaultIfEmpty() : joinedTblB)
             select new {
                tblA.ValueA,
                tblNEWB.ValueB
             });
  

Я надеялся, что это сработает, но я получаю сообщение об ошибке «InvalidOperationException: система доступа к участникам.Строковое значениеb’ из ‘tblB’ недопустимо для системы типа ‘.Коллекции.Общий.IEnumerable’1 [tblB]»

Я что-то здесь упускаю?

Обновить:

То, что я хочу, это внешнее соединение, но linq не выдал запрос, на который я надеялся, когда для tlbB заданы условия. Включение SQL Profiler выдает этот запрос:

 LEFT OUTER JOIN tblB ON tblA.Ref = tblB.REfA AND tlbB.Key = '100'
  

В то время как правильный запрос должен быть:

 LEFT OUTER JOIN tblB ON tblA.Ref = tblB.RefA
WHERE tblB.Key = '100'
  

Причиной этого является моя GetMyTable-функция, которая задает условие для таблицы в соединении.

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

1. Я не могу понять, что именно вы пытаетесь сделать, но знайте: tblB выходит за рамки после инструкции into. Затем вы вводите другую переменную диапазона для joinedTblB с тем же именем.

2. Почему бы не выполнить два отдельных запроса и не объединить их вместе?

3. Также это не внутреннее соединение, когда вы используете ключевое слово into. Четко объясните, что вы хотите сделать, и мы сможем помочь.

4. Я попытаюсь объяснить. Я хочу выполнить ВНУТРЕННЕЕ СОЕДИНЕНИЕ, когда в таблице есть условия, и я хочу выполнить ВНЕШНЕЕ СОЕДИНЕНИЕ, когда в той же таблице нет условий. Из-за сложности кода и условий, которые могут быть установлены, я надеялся, что смогу выполнить это в части соединения запроса, а не в части WHERE .

5. Вы на сто процентов уверены в том, что вы называете внешними и внутренними объединениями? То, чего вы, похоже, хотите достичь, выглядит как left join, который принимает все записи из первого набора и объединяет их с записями из второго набора, если они существуют на основе условия или null в противном случае

Ответ №1:

Я хочу объединить это в одном запросе и, проверив некоторое условие, выполнить либо ВНЕШНЕЕ СОЕДИНЕНИЕ, либо ВНУТРЕННЕЕ СОЕДИНЕНИЕ

Первый шаг к получению того, что вы просите, — это признать, что требуются два разных оператора sql. LinqToSql не собирается отправлять ваше условие в базу данных, чтобы база данных могла определить, какое соединение должно произойти, на основе значения.

Второй шаг — переключиться на синтаксис метода. Этот синтаксис более удобен для компоновки по условиям.

Третий шаг — отказаться от анонимных типов. Вам приходится делать неприятные вещи, чтобы объявлять переменные, необходимые для построения запроса, с теми, которые работают. Просто создайте тип с нужными вам свойствами и используйте его.

 public class AandB
{
  public A TheA {get;set;}
  public B TheB {get;set;}
}

IQueryable<A> queryA =  dc.TblA.AsQueryable();

IQueryable<AandB> queryAandB = null;

if (checkCondition)
{
  //inner join
  queryAandB = queryA
    .Join(
      GetMyTable(),
      a => a.Ref, b => b.RefA,
      (a, b) => new AandB() {TheA = a, TheB = b}
    );
}
else
{
  // left join
  queryAandB = queryA
    .GroupJoin(
      GetMyTable(),
      a => a.Ref, b => b.RefA,
      (a, g) => new {a, g}
    )
    .SelectMany(
      x => x.g.DefaultIfEmpty(),
      (x, b) => new AandB(){TheA = x.a, TheB = b}
    );
}

List<AandB> results = queryAandB.ToList();
  

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

1. Я надеялся, что Linq «сделает работу» за меня. Спасибо за информацию, я, вероятно, пойду с этим решением.

Ответ №2:

если вам нужно левое соединение, ваш первый запрос должен выполнить эту работу

         (from tblA in dc.tblA
         join tblB in GetMyTable() on tblA.Ref equals tblB.RefA into joinedTblB
         from tblB in joinedTblB.DefaultIfEmpty()
         select new {tblAField = tblA.F1 , tblBField = tblB == null ? null : tblB.F2);
  

запрос внутреннего соединения будет выглядеть следующим образом

         (from tblA in dc.tblA
         join tblB in GetMyTable() on tblA.Ref equals tblB.RefA 
         select new { A = tblA, B = tblB } );
  

Ответ №3:

Я не уверен, что это именно то, что вы ищете, но оно должно быть близко.

 var query = from rowA in db.tblA
            join rowB in db.tblB on rowA.idB equals rowB.idB into b
            from item in b.DefaultIfEmpty()
            select new
            {
                rowA.ValueA,  
                rowB.ValueB
            };