Во-первых, давайте уменьшить вашу проблему до минимума случай:
У вас есть следующие типы:
public class A
{
public int Id { get; set; }
public List<B> bs = new List<B>();
public List<C> cs = new List<C>();
}
public class B
{
public int CPMCId { get; set; }
}
public class C
{
public int CPMCId { get; set; }
}
Видимо, у вас есть список A
-х, B
х и C
s
List<A> as;
List<B> bs;
List<C> cs;
ты смотришь g, чтобы создать список A
Теперь давайте посмотрим, почему ваше решение идет медленно.
Что вы делаете, сначала создайте список всех желаемых идентификаторов, а затем для каждого идентификатора найдите все записи, которые соответствуют. Это означает, что вы просматриваете дочерние списки целиком для каждого идентификатора. Это явно не оптимально.
Операция, которую вы ищете, называется Outer Join
в SQL
. К сожалению, Linq не имеет эквивалентной операции из коробки.
Итак, мы сами это сделаем. Можно сделать общую версию этого подхода, но это не совсем просто. То, что мы собираемся сделать, это своего рода А-х и B, вольны CPMCId
, а затем принять все нужные записи, которые имеют соответствующий идентификатор в списке A
с:
IEnumerable<A> make_as(IEnumerator<B> ordered_bs, IEnumerator<C> ordered_cs, IEnumerator<int> ordered_ids) {
//make sure the current element of bs and cs is at the first element, not before it.
if(!ordered_bs.MoveNext() || !ordered_cs.MoveNext())
throw new ArgumentException("empty bs or cs");
while(ordered_ids.MoveNext()) {
nextid = ordered_ids.Current;
var a = new A(){
id = nextId;
};
//process the B's
while(ordered_bs.Current.CPMCId < nextid) //not in the list, skip it {
ordered_bs.MoveNext();
}
while(ordered_bs.Current.CPMCId == nextid) //matching, add to the list {
a.bs.add(ordered_cs.Current);
if(!orderd_bs.MoveNext()) break; //move bs forward. If b's is empty, we're done here
}
//do the same for the C's
while(ordered_cs.Current.CPMCId < nextid) {
ordered_cs.MoveNext();
}
while(ordered_cs.Current.CPMCId == nextid) {
a.cs.add(ordered_cs.Current);
if(!ordered_cs.MoveNext()) break;
}
yield return a;
}
}
var result = make_as(
bs.orderBy(b => b.PCMCId).GetEnumerator(),
cs.orderBy(c => c.PCMCId).GetEnumerator(),
as.Select(a => a.id).OrderBy(id => id).Distinct().GetEnumerator()
).ToList()
Некоторые примечания:
У меня создается впечатление, что это часть решения, которое уже обработано. Когда вы знаете, что вы будете нуждаться все идентификаторы, вам не нужен оригинальный список A
-х на всех, и NextID будет самый низкий Current
из A
-х и B
ы
Это также вполне возможно, что прямо сейчас вы находитесь в небольшой дыре, в которую вы врывались. Вполне возможно, что вы могли бы сделать это более эффективно - и более элегантно - далее «вверх по течению» в вашем коде.
В качестве последнего примечания этот фрагмент не работает, если либо список B
, либо список C
не содержат элементов. В этом случае достаточно простого GroupBy.
Вы не используете EntityFramework, а просто LINQ? Учитывая структуру и большой объем данных, я ожидал бы, что это будет медленным, потому что вы размещаете все в памяти (5k CPMCId и 50k записей для каждого дочернего списка) –
Я, я использую простой запрос linq. Что бы я сделал, чтобы сделать это быстрее – user1392853
Что такое tmpl и tpmcl и почему они не имеют hashmap-backed IDictionary (например, словарь)? –
moreON