Как и как ввести оператор "или" в соединение запроса linq - PullRequest
3 голосов
/ 04 марта 2020

Какой синтаксис вводит оператор "или" в соединение запроса linq. Это не похоже на "||", "или"? Мне нужно соединить один внешний ключ с двумя возможными ключами из таблицы пользователей.

var thirdQuery = (from u in tbl_users
join ua in tbl_userDocuments on (u.TransportUserID equals ua.fkDocumentID
                             || u.WorkUserID equals ua.fkDocumentID) into uaGroup
from uaTrans in uaGroup.DefaultIfEmpty()
join ul in tbl_user_logins on uaTrans.fkUserID equals ul.user_login_id_pk into ulGroup

Без части или это работает, но отсутствует дополнительный ключ

var thirdQuery = (from u in tbl_users
join ua in tbl_userDocuments on (u.TransportUserID equals ua.fkDocumentID) into uaGroup
from uaTrans in uaGroup.DefaultIfEmpty()
join ul in tbl_user_logins on uaTrans.fkUserID equals ul.user_login_id_pk into ulGroup

1 Ответ

1 голос
/ 04 марта 2020

Итак, у вас есть база данных по крайней мере с двумя таблицами: Users и UserDocuments. Каждый UserDocument имеет свойство fkDocumentId.

Хотя вы не сказали этого, мне кажется, что это внешний ключ к элементам в таблице Users. Очевидно, этот внешний ключ иногда относится к User.TransportUserId, а иногда к User.WorkUserId.

Вы уверены, что хотите этого? Если fkDocumentId имеет значение 10, относится ли оно к пользователю с TransportId, равным 10, или к пользователю с WorkUserId, равным 10, или к обоим?

В любом случае, если вы исследуете Enumerable .Join, тогда вы обнаружите, что вы предоставляете два keySelectors: один для выбора ключа из списка «Пользователи» и один для выбора ключа из документа «UserDocuments». Когда эти два ключа равны, тогда параметр ResultSelector используется для создания вашего присоединенного элемента.

Проблема в словах "равны". Вам нужно убедиться, что вы выбрали свои ключи и предоставили IEqualityComparer, так что ключи будут считаться равными.

Еще один более простой способ - создать новый метод расширения Join.

IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult> Join(
    this IEnumerable<TSource> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector1,
    Func<TOuter, TKey> outerKeySelector2,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, TInner, TResult> resultSelector)
{
    // make two lookup tables from Outer: one for key1 and one for key2:
    var lookup1 = outer.ToLookup( outerElement => outerKeySelector1(outerElement));
    var lookup2 = outer.ToLookup( outerElement => outerKeySelector2(outerElement));

    // so if you have a TKey from the innerKeySelector, you can find if it matches key1 or key2
    foreach (TInner innerElement in inner)
    {
        TKey innerKey = innerKeySelector(innerElement)
        var matchingElementsKey1 = lookup1[innerKey];
        var matchineElementsKey2 = lookup2[innerKey];
        var allmatchingElements = matchingElementsKey1.Concat(matchingElementsKey2);

        foreach(var matchingElement in allMatchingElements)
        {
            TResult result = resultSelector(matchingElement, innerElement);
            yield return result;
        }
    }
}

Использование:

var result = tblUsers.Join(tblUserDocuments,
    user => user.TransportUserId,           // select outerKey1
    user => user.WorkUserId,                // select outerKey2
    document => document.fkDocumentId,      // select innerKey

    // when inner key matches either outerKey1, or outerKey2, create one new object:
    (user, document) => new
    {
        // Select the user documents that you want:
        UserId = user.Id,
        Name = user.Name,
        ...

        // Select the document properties that you want:
        DocumentId = document.Id,
        Author = document.Author,
        PublishedDate = document.Date,
        ...
    })
...