Entity Framework + Linq LEFT JOIN с использованием предложения where?

Я использую Linq с Entity Framework 5 и использую синтаксис запроса (думаю, исправьте мою терминологию). У меня есть таблица участников, и я хочу связать их идентификационный номер исследования с зарегистрированным SMS-сообщением, где номер «кому» или «от» в сообщении соответствует номеру телефона участника. Кроме того, я хочу, чтобы сообщения, отправленные с (или на) неизвестного номера, также отображались в списке, и в этом случае идентификационный номер исследования будет нулевым.

Вот рабочий необработанный запрос для LEFT JOIN (с использованием базы данных MySQL, если это имеет значение):

SELECT
    messages._id, participants.study_id_number, messages.ts,
    messages.from_phone, messages.to_phone, messages.body
FROM messages LEFT JOIN
    participants ON (   (participants.phone_number = messages.to_phone)
                     || (participants.phone_number = messages.from_phone))
ORDER BY messages.ts DESC;

Вот что я до сих пор работаю в Linq, но это внутреннее соединение:

var loggedMessages = from pp in theDb.participants
                     let phone = pp.phone_number
                     from mm in theDb.messages
                     let fromPhone = mm.from_phone
                     let toPhone = mm.to_phone
                     where ((phone == fromPhone) || (phone == toPhone))

                     orderby mm.ts descending
                     select new MessageLogEntry()
                     {
                         ParticipantId = pp.study_id_number,
                         TimeStamp = mm.ts,
                         FromPhone = fromPhone,
                         ToPhone = toPhone,
                         Body = mm.body
                     };

Я новичок в этом, поэтому я был бы рад получить ссылку на учебник по запросам Linq, но что мне нужно добавить, чтобы сделать его ЛЕВЫМ СОЕДИНЕНИЕМ?

РЕДАКТИРОВАТЬ: Пожалуйста, смотрите мой собственный ответ ниже.


person William T. Mallard    schedule 16.02.2014    source источник
comment
Действительно, есть руководство от Microsoft: msdn.microsoft.com/en-us/ библиотека/bb397895.aspx   -  person Michael Dunlap    schedule 16.02.2014
comment
codingsense.wordpress.com/2009/03 /08/   -  person Wagner DosAnjos    schedule 16.02.2014
comment
@Michael Dunlap, это для левых ВНЕШНИХ соединений, которые, если я правильно понимаю терминологию соединения, дадут мне список участников, которые не отправляли и не получали никаких сообщений, да? Кто-нибудь еще находит это запутанным?   -  person William T. Mallard    schedule 16.02.2014
comment
@wdosanjos Кажется, это указывает мне правильное направление, то есть RFM. Спасибо.   -  person William T. Mallard    schedule 16.02.2014
comment
@MichaelDunlap Извинения, по-видимому, не все определения терминологии соединения различают простой левый (все из набора один и совпадающие элементы набора 2) и левый внешний (только те элементы в наборе один, для которых нет соответствия в наборе 2). Перечитываю сейчас...   -  person William T. Mallard    schedule 16.02.2014


Ответы (2)


Вам нужно добавить DefaultIfEmpty(), чтобы сделать его левым соединением.

Проверь это:

var loggedMessages = from pp in theDb.participants
                     join mm in theDb.messages
                     on pp.phone_number equals mm.to_phone || 
                     pp.phone_number equals mm.from_phone
                     into joinedmm
                     from pm in joinedmm.DefaultIfEmpty()
                     orderby mm.ts descending
                     select new MessageLogEntry()
                     {
                         ParticipantId = pp.study_id_number,
                         TimeStamp = pm.ts,
                         FromPhone = fromPhone,
                         ToPhone = toPhone,
                         Body = pm.body
                     };
person Bappi Datta    schedule 16.02.2014
comment
Спасибо, но я намного опередил вас. :) В синтаксисе соединения нет или. Это была проблема №1. Проблема № 2 заключается в том, что это левое соединение, и добавление from pp in the Db.participants.DefaultIfEmpty(), похоже, не работает. - person William T. Mallard; 16.02.2014

После некоторого быстрого и неглубокого изучения, а также еще нескольких ссылок SO, у меня есть следующее, что приводит к желаемому результату. Комментарии очень приветствуются, самое главное, что я узнал из этого упражнения, это то, как много мне еще предстоит узнать!

var loggedMessages = from mm in theDb.messages
                     from pp in theDb.participants
                     .Where(pp1 => ((mm.from_phone == pp1.phone_number) || (mm.to_phone == pp1.phone_number)))
                     .DefaultIfEmpty()
                     orderby mm.ts descending
                     select new MessageLogEntry()
                     {
                         ParticipantId = (int?)pp.study_id_number,
                         TimeStamp = mm.ts,
                         FromPhone = mm.from_phone,
                         ToPhone = mm.to_phone,
                         Body = mm.body
                     };

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

И LinqPad просто великолепен!

person William T. Mallard    schedule 16.02.2014