2016-07-08 1 views
1

У меня есть эта модельLinq заполнить данные для вложенного списка <T>

public class CPMC 
{ 
    public int CPMCId { get; set; } 
    public List<TPM> tpm = new List<TPM>(); 
    public List<TPMC> tpmc = new List<TPMC>(); 
} 
public class TPMC 
{ 
    public int Id { get; set; } 
    public Int64 Amount { get; set; } 
    public int PId { get; set; } 
    public Int64 PAmount { get; set; } 
    public int CPMCId { get; set; } 
} 
public class TPM 
{ 
    public int Type { get; set; } 
    public int Id { get; set; } 
    public Int64 Amount { get; set; } 
    public int VAT { get; set; } 
    public DateTime FromDate { get; set; } 
    public DateTime ToDate { get; set; } 
    public int CPMCId { get; set; } 
} 

Данные для этого списка является 5k записи CPMCId и 50к записей для каждого списка ребенка внутри с условием является

List<int> CPMCIdList = aPP.Select(x => Convert.ToInt32(x.CPMCId)).Distinct().ToList(); 
       List<CPMC> cpl = (from ap in CPMCIdList 
            select new CPMC 
            { 
             CPMCId = ap, 
             tpm = tpml.Where(x=>x.CPMCId == ap).ToList(), 
             tpmc = tpmcl.Where(x=>x.CPMCId == ap).ToList() 
            }).ToList(); 

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

+1

Вы не используете EntityFramework, а просто LINQ? Учитывая структуру и большой объем данных, я ожидал бы, что это будет медленным, потому что вы размещаете все в памяти (5k CPMCId и 50k записей для каждого дочернего списка) –

+0

Я, я использую простой запрос linq. Что бы я сделал, чтобы сделать это быстрее – user1392853

+0

Что такое tmpl и tpmcl и почему они не имеют hashmap-backed IDictionary (например, словарь)? – moreON

ответ

2

Благодаря двум внутренним линейным цикл запросов (LINQ Where операторы), текущая реализация имеет O(K*N*M) временную сложность, где K=CPMCIdList.Count, N=tpml.Count, M=tpmcl.Count.

Это может быть сведено к гораздо быстрее O(K+M+N) с помощью операторов LINQ Group Join, которые внутренне используют довольно эффективный хэш-поиск на основе:

var cpl = 
    (from cpmcId in CPMCIdList 
    join tpm in tpml on cpmcId equals tpm.CPMCId into tpmg 
    join tpmc in tpmcl on cpmcId equals tpmc.CPMCId into tpmcg 
    select new CPMC 
    { 
     CPMCId = cpmcId, 
     tpm = tpmg.ToList(), 
     tpmc = tpmcg.ToList() 
    }).ToList(); 
+0

Это кажется хорошим, спасибо за участие в группе. Я принимаю этот ответ – user1392853

2

Во-первых, давайте уменьшить вашу проблему до минимума случай:

У вас есть следующие типы:

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.

+0

Caveat Emptor! Этот фрагмент был скомпилирован, протестирован и проверен только на мой взгляд. – Martijn

+0

Нет, это слишком сложно, я использую ответ @Ivan Stoev и получаю лучшее определение с помощью простого инструмента. Спасибо – user1392853

Смежные вопросы