Как кроме <> указать другой ключ?Или более быстрый способ отличия двух огромных списков <>? - PullRequest
0 голосов
/ 31 января 2019

У меня есть список AE_AlignedPartners элементов в БД, который я извлекаю с помощью:

List<AE_AlignedPartners> ae_alignedPartners_olds = ctx.AE_AlignedPartners.AsNoTracking().ToList();

Затем я получил и сериализовал новый список (того же типа объекта) с помощью JSON:

List<AE_AlignedPartners> ae_alignedPartners_news = GetJSONPartnersList();

Чем я получаю пересечения обоих:

var IDSIntersections = (from itemNew in ae_alignedPartners_news
                        join itemOld in ae_alignedPartners_olds on itemNew.ObjectID equals itemOld.ObjectID
                        select itemNew).Select(p => p.ObjectID).ToList();

Теперь, из-за этих пересечений, мне нужно создать два новых списка с добавленными элементами (ae_alignedPartners_news - пересечения) и удаленные (ae_alignedPartners_olds - interesections).Вот код:

// to create
IList<AE_AlignedPartners> ae_alignedPartners_toCreate = ae_alignedPartners_news.Where(p => !IDSIntersections.Contains(p.ObjectID)).ToList();

// to delete
IList<AE_AlignedPartners> ae_alignedPartners_toDelete = ae_alignedPartners_olds.Where(p => !IDSIntersections.Contains(p.ObjectID)).ToList();

Но со многими записями (~ 100 КБ) это слишком много времени.

Есть ли какая-то Except<>, указывающая, какой ключ нужно сравнивать?В моем случае это не p.ID (то есть Primary Key на БД), а p.ObjectID.

Или любой другой более быстрый способ?

Ответы [ 2 ]

0 голосов
/ 31 января 2019

Вам не нужно внутреннее соединение, вам нужно полное внешнее соединение по первичному ключу.LINQ не знает полного внешнего соединения, но его легко расширить с помощью функции.

из StackOverlow: полное внешнее соединение LINQ , я выбрал решение, которое использует отложенное выполнение.Это решение работает, только если KeySelector использует уникальные ключи.

    public static IEnumerable<TResult> FullOuterJoin<TA, TB, TKey, TResult>(
        this IEnumerable<TA> sequenceA,
        IEnumerable<TB> sequenceB,
        Func<TA, TKey> keyASelector, 
        Func<TB, TKey> keyBSelector,
        Func<TKey, TA, TB, TResult> resultSelector,
        IEqualityComparer<TKey> comparer)
{
    if (comparer == null) comparer = EqualityComparer<TKey>.Default;

    // create two lookup tables:
    var alookup = a.ToLookup(selectKeyA, comparer);
    var blookup = b.ToLookup(selectKeyB, comparer);

    // all used keys:
    var aKeys = alookup.Select(p => p.Key);
    var bKeys = blookup.Select(p => p.Key);
    var allUsedKeys = aKeys.bKeys.Distinct(comparer);

    // for every used key:
    // get the values from A with this key, or default if it is not a key used by A
    // and the value from B with this key, or default if it is not a key used by B
    // put the key, and the fetched values in the ResultSelector
    foreach (TKey key in allUsedKeys)
    {
        TA fetchedA = aLookup[key].FirstOrDefault();
        TB fetchedB = bLookup[key].FirstOrDefault();
        TResult result = ResultSelector(key, fetchedA, fetchedB);
        yield result;
    }

Я использую эту функцию для создания трех типов:

  • Значения в A, но не в B: (A, ноль) => необходимо добавить
  • Значения в B, но не в A: (null, B) => должны быть удалены
  • Значения в A и B: (A, B) =>необходима дополнительная проверка, чтобы выяснить, требуется ли обновление

.

IEnumerable<AlignedPartners> olds = ...
IEnumerable<AlignedPartners> news = ...

var joinResult = olds.FullOuterJoin(news, // join old and new
    oldItem => oldItem.Id,                // from every old take the Id
    newItem => newItem.Id,                // from every new take the Id
    (key, oldItem, newItem) => new        // when they match make one new object
    {                                     // containing the following properties
         OldItem = oldItem,
         NewItem = newItem,
    });

Примечание: до сих пор ничего не было перечислено!

foreach (var joinedItem in joinResult)
{
    if (joinedItem.OldItem == null)
    {
        // we won't have both items null, so we know NewItem is not null
        AddItem(joinedItem.NewItem);
    }
    else if (joinedItem.NewItem == null)
    {   // old not null, new equals null
        DeleteItem(joinedItem.OldItem);
    }
    else
    {  // both old and new not null, if desired: check if update needed
        if (!comparer.Equals(old, new))
        {   // changed
            UpdateItems(old, new)
        }
    }
}
0 голосов
/ 31 января 2019

Существует функция Кроме , которую можно использовать с пользовательским компаратором:

    class PartnerComparer : IEqualityComparer<AE_AlignedPartners>
    {
        // Partners are equal if their ObjectID's are equal.
        public bool Equals(AE_AlignedPartners x, AE_AlignedPartners y)
        {         
            //Check whether the partner's ObjectID's are equal.
            return x.ObjectID == y.ObjectID;
        }

        public int GetHashCode(AE_AlignedPartners ap) {
            return ap.ObjectId.GetHashCode();
        }
    }

   var intersect = ae_alignedPartners_news.Intersect(ae_alignedPartners_olds);
   var creates = ae_alignedPartners_news.Except(intersect, new PartnerComparer);
   var deletes = ae_alignedPartners_old.Except(intersect, new PartnerComparer);

Это должно дать вам разумное повышение производительности.

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