Удалите «повторяющиеся» результаты с помощью Linq в LEFT OUTER joins - PullRequest
1 голос
/ 23 января 2020

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

У меня есть база данных контактов, которую я хочу найти ...

Каждый контакт связан с одним человеком (игнорировать отношения учреждения на данный момент) Каждый контакт также связан с несколькими записями электронной почты и телефона в других таблицах (каждое сообщение электронной почты и телефон являются исключительными для записи контакта).

enter image description here

Я хотел бы иметь возможность вернуть контакт, удовлетворяющий запросу, к записям электронной почты и / или телефонным записям. Таким образом, контакт может иметь несколько адресов электронной почты. Если мой запрос «попадет» на ЛЮБОЕ из электронных писем, я хочу, чтобы ОДИН контакт вернулся со ВСЕМИ электронными письмами (и телефонами), связанными с контактом, включенным в результаты.

Поэтому я ищу в почтовом домене «hollywood» , Существует один контакт с доменом электронной почты, равным «Голливуд». Этот контакт также имеет два других адреса электронной почты, связанных с ним. Я хочу вернуть одну запись, содержащую все эти записи. (Я фанат Моргана Фримена, поэтому я составил следующие данные - это ЛОЖЬ!)

enter image description here

Конечно, я выполняю такой запрос в SQL Server Management Studio вернет вам 3 записи. Один для каждой записи электронной почты, связанной с контактом. Однако я проецирую результаты в один объект, в который будут помещены несколько электронных писем (и телефонов), чтобы мне не приходилось иметь дело с несколькими объектами для одного и того же контакта.

Запрос построен в Linq. Я добавил строку "группа", думая, что она устранит дубликаты ...

var query = from rContact in qryContacts
            group rContact by rContact.contact_id into grpContact

            join rPerson in ctx.people
                on grpContact.FirstOrDefault().person_id equals rPerson.person_id

            join rEmail in ctx.emails
                on grpContact.FirstOrDefault().contact_id equals rEmail.contact_id into emailGroup
                from subEmails in emailGroup.DefaultIfEmpty()

            select new PersonalContact
            {
                ID          = grpContact.FirstOrDefault().contact_id,
                Label       = grpContact.FirstOrDefault().label,
                Notes       = grpContact.FirstOrDefault().notes,
                Prefix      = rPerson.prefix,
                FirstName   = rPerson.first_name,
                MiddleName  = rPerson.middle_name,
                LastName    = rPerson.last_name,
                Suffix      = rPerson.suffix,
                AKA         = rPerson.aka,
                DOB         = rPerson.dob,
                IsFemale    = rPerson.is_female,
                Emails      = emailGroup.Select(e=> new Email
                    {
                        ID              = e.email_id,
                        Label           = e.label,
                        Notes           = e.notes,
                        Preferred       = e.is_preferred,
                        Category        = e.email_category,
                        LocalPart       = e.email_local_part,
                        Domain          = e.email_domain,
                        TopLevelDomain  = e.email_top_level_domain,
                    }),
            }

Это нормально ... возвращает объект PersonalContact, заполненный как и ожидалось ... только он возвращает 3 из них! Я думал, что группировка в Контакте устранит дубликаты, но ... Нет.

Вот как выглядит результат (я только вставил один, но вернул три) ...

{
    "FirstName": "Morgan",
    "LastName": "Freeman",
    "DOB": "1937-06-01T00:00:00",
    "Prefix": "Mr",
    "IsFemale": false,
    "ID": 18,
    "Label": "Personal Contact",
    "Emails": [{
        "ID": 12,
        "LocalPart": "mfreeman",
        "Domain": "hollywood",
        "TopLevelDomain": "com",
        "Category": "Personal",
        "Preferred": true,
        "Display": "mfreeman@hollywood.com"
    },
    {
        "ID": 13,
        "LocalPart": "morgan.freeman",
        "Domain": "studiocity",
        "TopLevelDomain": "net",
        "Category": "Personal",
        "Preferred": false,
        "Display": "morgan.freeman@studiocity.net"
    },
    {
        "ID": 14,
        "LocalPart": "bob.smith",
        "Domain": "gmail",
        "TopLevelDomain": "com",
        "Category": "Private",
        "Preferred": false,
        "Display": "bob.smith@gmail.com"
    }],
}

Проблема только усугубляется, так как я добавляю несколько телефонов к контакту.

Любая помощь будет признательна!

ОБНОВЛЕНИЕ

Я думал стоило ответить на вопрос NetMage " Что такое qryContacts? Где вы фильтровали по адресу электронной почты? "

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

var contactFilter = new List<Expression<Func<contact, bool>>>();
    contactFilter.Add(e => e.emails.Where(x=>x.email_domain == "gmail").FirstOrDefault() != null);

Затем я получаю IQueryable, представляющий всю таблицу.

var qryContacts = ctx.contacts;

Теперь я добавляю содержимое фильтра в запрос.

if (contactFilter != null)
{
    foreach (var item in contactFilter)
    {
        qryContacts = qryContacts.Where(item);
    }
}

Затем запрос построен поверх этого, как показано в оригинальном сообщении. Надеюсь, это полезно. У него есть некоторые ограничения, но это был очень продуктивный шаблон для меня, облегчающий задачу написания запросов, когда вы точно не знаете, какие поля будут определены. Мое понимание основ Linq и Expressions очень ограничено, поэтому я обнаружил, что эту технику гораздо проще реализовать, чем некоторые из «Строителей». Что вы думаете об этой технике? Хотелось бы услышать некоторые отзывы.

1 Ответ

0 голосов
/ 23 января 2020

Решением проблемы (для меня) было удаление линии from subEmails in emailGroup.DefaultIfEmpty() ... Я должен быть честным, я не совсем уверен, почему это изменило ситуацию, но теперь все работает, как ожидалось.

var query = from rContact in qryContacts
    group rContact by rContact.contact_id into grpContact

    join rPerson in ctx.people
        on grpContact.FirstOrDefault().person_id equals rPerson.person_id

    join rEmail in ctx.emails
        on grpContact.FirstOrDefault().contact_id equals rEmail.contact_id into emailGroup
        // from subEmails in emailGroup.DefaultIfEmpty() <!-- REMOVE THIS LINE

    select new PersonalContact
    {
        ID          = grpContact.FirstOrDefault().contact_id,
        Label       = grpContact.FirstOrDefault().label,
        Notes       = grpContact.FirstOrDefault().notes,
        Prefix      = rPerson.prefix,
        FirstName   = rPerson.first_name,
        MiddleName  = rPerson.middle_name,
        LastName    = rPerson.last_name,
        Suffix      = rPerson.suffix,
        AKA         = rPerson.aka,
        DOB         = rPerson.dob,
        IsFemale    = rPerson.is_female,
        Emails      = emailGroup.Select(e=> new Email
            {
                 ID              = e.email_id,
                 Label           = e.label,
                 Notes           = e.notes,
                 Preferred       = e.is_preferred,
                 Category        = e.email_category,
                 LocalPart       = e.email_local_part,
                 Domain          = e.email_domain,
                 TopLevelDomain  = e.email_top_level_domain,
            }),
    }

Один человек проголосовал за эту ФП, так что, надеюсь, им удастся найти что-то полезное в этом. Желаем удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...