2014-03-26 4 views
1

У меня есть список деталей (200 000 записей), который был извлечен из базы данных, и мне нужно найти места для каждой детали, а ниже - код, который перебирает список деталей и присвоение места списку. Этот цикл занимает более 15 минут, но если он не заполняет свойство Locations, это занимает меньше минуты.LINQ to Objects Performance - Огромный набор данных для долгого процесса

Как я могу оптимизировать этот код?

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Details> databaseDetailList = GetDetailsFromdatabase(); 
     List<Location1> databaseLocation1List = GetLocations1Fromdatabase(); 
     List<Location2> databaseLocation2List = GetLocations2Fromdatabase(); 

     List<Details> detailList = new List<Details>(); 
     foreach (var x in databaseDetailList) 
     { 
      detailList.Add(new Details 
      { 
       DetailId = x.DetailId, 
       Code = x.Code, 
       //If I comment out the Locations then it works faster 
       Locations = new LocationIfo { 
        Locations1 = databaseLocation1List 
           .Where(l=>l.DetailId == x.DetailId && l.Code == x.Code).ToList(), 
        Locations2 = databaseLocation2List 
           .Where(l => l.DetailId == x.DetailId && l.Code == x.Code).ToList() 
       } 
      }); 
     } 
    } 

    private static List<Details> GetDetailsFromdatabase() 
    { 
     //This returns 200,000 records from database 
     return new List<Details>(); 
    } 

    private static List<Location1> GetLocations1Fromdatabase() 
    { 
     //This returns 100,000 records from database 
     return new List<Location1>(); 
    } 

    private static List<Location2> GetLocations2Fromdatabase() 
    { 
     //This returns 100,000 records from database 
     return new List<Location2>(); 
    } 
} 

public class Details 
{ 
    public string DetailId { get; set; } 
    public string Code { get; set; } 
    public LocationIfo Locations { get; set; } 
} 

public class LocationIfo 
{ 
    public List<Location1> Locations1 { get; set; } 
    public List<Location2> Locations2 { get; set; } 
} 

public class Location1 
{ 
    public int LocationId { get; set; } 
    public string DetailId { get; set; } 
    public string Code { get; set; } 
    public string OtherProperty { get; set; } 
} 

public class Location2 
{ 
    public int LocationId { get; set; } 
    public string DetailId { get; set; } 
    public string Code { get; set; } 
    public string OtherProperty { get; set; } 
} 
+0

Почему 'Location1' точно так же, как' Location2'? Можете ли вы использовать LINQ to SQL или LINQ для Entities? –

+0

Это пример кода. У меня есть 2 набора объектов, которые имеют много похожих свойств. для простоты я сохранил те же свойства. – SnowWhite

ответ

4

Что вы делаете концептуально здесь: Join. Использование надлежащих операций гарантирует, что он будет выполняться намного эффективнее. В идеале вы даже делаете Join на стороне базы данных, а не на то, чтобы вытащить все данные в списки, но даже если вы потянете все данные вниз, объединение их в память с помощью Join будет много более эффективным.

var query = from detail in databaseDetailList 
      join location1 in databaseLocation1List 
      on new { detail.DetailId, detail.Code } 
      equals new { location1.DetailId, location1.Code } 
      into locations1 
      join location2 in databaseLocation2List 
      on new { detail.DetailId, detail.Code } 
      equals new { location2.DetailId, location2.Code } 
      into locations2 
      select new Details 
      { 
       Code = detail.Code, 
       DetailId = detail.DetailId, 
       Locations = new LocationIfo 
       { 
        Locations1 = locations1.ToList(), 
        Locations2 = locations2.ToList(), 
       } 
      }; 
+0

Но [Декартовой продукт] (http://en.wikipedia.org/wiki/Cartesian_product) будет 2 000 000 000 000 000, возвращая все объединенные строки. Это по-прежнему лучшая идея, но создание трех вызовов, возвращающих каждую таблицу (но присоединившихся), было бы идеальным. –

+0

Я знаю, что это операция соединения, и мне нужно заполнить объект detailList. GetDetailsFromdatabase(), GetLocations1Fromdatabase(), GetLocations2Fromdatabase() - это все вызовы служб. можете ли вы предоставить примерный код о том, что вы думаете. Я не понимаю. – SnowWhite

+0

@Servy, Спасибо за образец кода. Я тестировал его, и это работает. – SnowWhite

0

Это немного Hacky - но вы могли бы использовать средства поиска:

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

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

DetailId-Code - "123123-343" 


private static ILookup<int, Location1> GetLocations1Fromdatabase() 
{ 
    //This returns 100,000 records from database 
    return new List<Location2>() 
     .ToLookup(l => l.DetailId + "-" + l.Code); 
} 

Тогда:

Locations1 = databaseLocation1List[l.DetailId + "-" + l.Code].ToList() 
+1

Использование поиска не является взломанным вообще. Это абсолютно правильное решение проблемы. Согласование значения двух строк для создания ключа крайне хакерское, подверженное ошибкам и не должно выполняться. Вы должны создать (или использовать существующий) составной тип со значимыми реализациями равенства и генерации хэш-кода. – Servy

+0

Почему бы не использовать анонимный объект, а не конкатенацию строк?Сможет ли беспокоиться об использовании «-» в качестве магических символов и т. Д., Также я думаю, что существует перегрузка ToLookup, которая делает параметр l => l ненужным. – Chris

+0

@Chris Уже есть [другой ответ] (http://stackoverflow.com/a/22667590/1159478) (опубликовано до этого ответа), который делает именно это. – Servy

0

Это a very similar problem я столкнулся пару дней назад (в Ruby). Наконец самое лучшее решение, которое я нашел в том, чтобы преобразовать список (или массив) в словарь (или Hash)

Создать словарь с вашими поисковыми полями сцепленных как ключ и список ваш товар как в качестве значения:

var dict1 = databaseLocation1List.ToDictionary(x => x.DetailId.ToString() + x.Code.ToString(), x); 

И тогда поиск в словаре по ключу будет очень быстро:

Locations1 = dict1[x.DetailId.ToString() + x.Code.ToString()].ToList() 
+0

Причина, по которой я предложил Lookup, как и в моем ответе, - это то, что, как представляется, для каждого Id-комбо есть несколько значений, поэтому .ToList() –

+0

Использование поиск совсем не хаки. Это абсолютно правильное решение проблемы. Согласование значения двух строк для создания ключа крайне хакерское, подверженное ошибкам и не должно выполняться. Вы должны создать (или использовать существующий) составной тип со значимыми реализациями равенства и генерации хэш-кода. – Servy

+0

Я понимаю, спасибо и за комментарии! –

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