LINQ ЛЕВОЕ ВНЕШНЕЕ соединение, ГДЕ

#c# #linq #linq-to-sql

#c# #linq #linq-to-sql

Вопрос:

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

Объединение и группировка кажутся довольно простыми, однако, когда я, наконец, пытаюсь запустить оператор Select и поместить все различные поля из обеих таблиц, для меня все, кажется, разваливается. Я надеюсь, что смогу опубликовать некоторый SQL-код, затем мою попытку собрать выражение LINQ вместе, и люди здесь смогут ответить на мои вопросы о том, КАК это в принципе должно работать. Почти уверен, что LINQ ПОЛНОСТЬЮ ИСПОРЧЕН. :-/

У меня есть следующий оператор SQL, который предоставляет мне выбор данных, которые я хотел бы затем использовать и передать в элемент управления grid. (Однако с использованием LINQ для объектов)

Вот инструкция SQL, которую я пытаюсь эмулировать в LINQ:

 SELECT s.STRUCTURE_ID, s.TITLE, s.PHOTO, s.PHOTO_LINK, COUNT(s.STRUCTURE_ID)
   AS "How Many Activities", MIN(m.START_DATE) AS "START DATE", MAX(m.FINISH_DATE) AS "FINISH DATE"
FROM DB.STRUCTURES s
LEFT OUTER JOIN DB.ACTIVITIES m ON s.STRUCTURE_ID = m.STRUCTURE_ID
WHERE s.PARK = 'Elwood' AND m.CONTRACT_NO IS NOT NULL
GROUP BY  s.STRUCTURE_ID, s.TITLE, m.STATUS, s.PHOTO, s.PHOTO_LINK
ORDER BY s.STRUCTURE_ID
  

DB.Structures — это ОДИН файл

DB.Activities — это МНОЖЕСТВО файлов

Цель выбора — выбрать все структуры, которые имеют действия, соответствуют определенному парку и ДЕЙСТВИТЕЛЬНО имеют контракт. Дополнительный небольшой поворот заключается в получении самых ранних дат начала и окончания связанных действий для каждой структуры.

Результирующие наборы данных должны представлять собой сглаженную коллекцию, которую я могу просто передать своему элементу управления grid в следующем формате:

 Structure ID     Description           Photo      Start Date     Finish Date
HL-100           Activity Room 100     Yes        6/6/2011       8/26/2011
HS-400A          Small Ones Gym        No         5/2/2011       6/30/2011
  

Итак, LINQ, который я собрал, выглядит следующим образом:

 var query = from s in db.Structures
            join a in db.Activities on s.Structure_ID equals a.Structure_ID into sa
            from allStructs in sa.DefaultIfEmpty()       // I believe this is how to get the outer join and flatten the selection?
            where s.PARK == 'Elwood' amp;amp; allStructs.CONTRACT_NO != ''
            orderby s.Structure_ID
            group s by s.Structure_ID into g
            select new
            {
                myID = g.Key,
                myDesc = //I don't know how to display the s.Title here ,
                myPhoto = // I don't know how to display the s.Photo here ,
                myStartDate = g.Min(c => c.START_DATE),
                myEndDate = g.Max(c => c.FINISH_DATE)
            };
  

Итак, теперь к вопросам:

  1. Одна вещь, которая мне не ясна в LINQ, не похоже, что после завершения объединения я получаю единственную коллекцию, содержащую ВСЕ объявленные столбцы, как я бы получил со стандартной инструкцией SQL, возвращающей выделение. Это сбивает меня с толку. Может ли кто-нибудь подсказать мне, правда это или нет? Похоже, я должен быть в состоянии «видеть» все столбцы в объекте ‘allStructs’…но это, очевидно, не тот случай.

  2. Другая серьезная проблема, с которой я борюсь, заключается в том, как отобразить столбцы из ОДНОГО файла — я не могу просто найти способ перейти к этим полям. (Какой вид связан с # 1 выше.)

  3. Наконец, есть ли какие-нибудь хорошие «трюки» или инструменты, с помощью которых вы можете пошагово просматривать каждую строку в выражении LINQ, как вы можете в отладчике для прямого кода C #? Когда это выражение LINQ попадает в обычный отладчик в VS, оно просто выделяет полное выражение, и вы действительно не можете видеть, что происходит при настройке / выполнении. (Я понимаю, что здесь это может потребовать многого.) В настоящее время я просто использую консоль, чтобы просмотреть что-либо после приведения выражения в действие, например, для выполнения запроса.Count() или что-то в этом роде.

Я был бы искренне признателен за любую помощь в получении лучшего понимания того, как данные преобразуются с помощью этого процесса LINQ. Я много читал здесь, MSDN и купил пару книг, но я не получил того «момента лампочки», который, кажется, все указывают, наступит.

Спасибо всем … 🙂

Ответ №1:

  1. После join у вас есть доступ к s (текущей структуре) и sa группе действий для этого Structure_ID . После DefaultIfEmpty() у вас будет доступ к тому же самому s и now allStructs , который будет точно соответствовать m вашему исходному примеру SQL. Вы никогда не получите единую переменную, которая представляет все столбцы в обеих таблицах, но вы также не получите этого в SQL.

  2. Я считаю, что LINQ to SQL достаточно умен, чтобы перевести группировку по составному ключу, и в этом случае это то, что вы, вероятно, хотите:

     var query = from s in db.Structures
                join a in db.Activities on s.Structure_ID equals a.Structure_ID into sa
                from m in sa.DefaultIfEmpty() // LEFT OUTER JOIN
                where s.PARK == 'Elwood' amp;amp; m.CONTRACT_NO != ''
                orderby s.Structure_ID
                group m by new { s.Structure_ID, s.Title, s.Photo } into g
                select new
                {
                    myID = g.Key.Structure_ID,
                    myDesc = g.Key.Title,
                    myPhoto = g.Key.Photo,
                    myStartDate = g.Min(c => c.START_DATE),
                    myEndDate = g.Max(c => c.FINISH_DATE)
                };
      

    Обратите внимание, что я переключился allStructs на m для согласованности с вашим SQL и переключился group by на использование m вместо s . m Значения — это то, что будет содержаться в g группах, причем Key они представляют собой кортеж из Structure_ID , Title и Photo .

  3. Для отладки лучше всего включить какое-нибудь ведение журнала ( DataContext.Log свойство), чтобы увидеть, какой SQL генерируется. Вы также можете использовать SQL profiler. Иногда это помогает логически разобраться с коллекциями в памяти и LINQ to Objects, с оговоркой, что вы можете создавать в памяти вещи, которые вообще не переводятся в SQL.

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

1. БОЛЬШОЕ спасибо за помощь и разъяснения по этому поводу. Я никогда раньше не видел эту команду ‘group m by new’, и для меня это было недостающей частью. Наилучшие пожелания … 🙂

Ответ №2:

Чтобы ответить на ваш второй вопрос, я думаю, вы можете сделать следующее:

 myDesc = g.First().Title
myPhoto = g.First().Photo
  

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

1. Привет, спасибо за это. Я пробовал это в нескольких местах, тестируя, чтобы увидеть, как это работает, и он действительно получает информацию, как вы описали! Наилучшие пожелания … 🙂