2015-06-30 2 views
2

Я понимаю, что AsEnumerable() используется для переключения с «LINQ to SQL» на «LINQ to Object», поэтому мы можем использовать некоторые дополнительные (в основном пользовательские) методы в наших запросах LINQ. Но из моего опыта, который я видел, использование AsEnumerable() делает запрос намного медленнее. В этом случае я могу перечислить список позже, чтобы применить свои собственные методы, но результат довольно медленный.Использовать ли ASEnumerable() в LINQ или нет?

Может ли кто-нибудь предложить лучший подход?

Вот пример кода того, что я пытаюсь сделать?

С AsEnumerable():

var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable() 
        //where r.RecordStatus == "NCF" 
        orderby r.RequisitionNo descending 
        select new PRDChemProdReq 
        { 
         RequisitionID = r.RequisitionID, 
         RequisitionNo = r.RequisitionNo, 
         RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory), 
         RequisitionType = DalCommon.ReturnOrderType(r.RequisitionType), 
         ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"), 
         RecordStatus= DalCommon.ReturnRecordStatus(r.RecordStatus), 
         RequisitionFromName = DalCommon.GetStoreName(r.RequisitionFrom), 
         RequisitionToName = DalCommon.GetStoreName(r.RequisitionTo) 
        }).ToList(); 

без AsEnumerable():

var Data = (from r in _context.PRD_ChemProdReq 
        //where r.RecordStatus == "NCF" 
        orderby r.RequisitionNo descending 
        select new PRDChemProdReq 
        { 
         RequisitionID = r.RequisitionID, 
         RequisitionNo = r.RequisitionNo, 
         RequisitionCategory = r.RequisitionCategory, 
         RequisitionType = (r.RequisitionType), 
         ReqRaisedOnTemp = (r.ReqRaisedOn), 
         RecordStatus= (r.RecordStatus), 
         RequisitionFrom = (r.RequisitionFrom), 
         RequisitionTo = (r.RequisitionTo) 
        }).ToList(); 

     foreach (var item in Data) 
     { 
      item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory); 
      item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType); 
      item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy"); 
      item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus); 
      item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom); 
      item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo); 
     } 
+2

XY проблема. Ваша настоящая проблема возникает из-за того, что вы смешиваете логику отображения с логикой доступа к данным. – Aron

+1

Не так много различий между двумя фрагментами кода, вы неизменно приносите данные в память один раз с помощью AsEnumerable, затем ToList(), а в другом случае с помощью ToList(). Вы можете найти разницу только при использовании IEnumerable и IQueryable отдельно. IQueryable поможет выполнить запрос через поставщика данных. –

ответ

0

OK ребята, я обратил внимание на различные моменты от всех вас & пришел с этим:

var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable() 
        //where r.RecordStatus == "NCF" 

        join rf in _context.SYS_Store on (r.RequisitionFrom==null?0: r.RequisitionFrom) equals rf.StoreID into requisitionfrom 
        from rf in requisitionfrom.DefaultIfEmpty() 

        join rt in _context.SYS_Store on (r.RequisitionTo == null ? 0 : r.RequisitionTo) equals rt.StoreID into requisitionto 
        from rt in requisitionto.DefaultIfEmpty() 

        orderby r.RequisitionNo descending 
        select new PRDChemProdReq 
        { 
         RequisitionID = r.RequisitionID, 
         RequisitionNo = r.RequisitionNo, 
         RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory), 
         RequisitionType = r.RequisitionType == "UR" ? "Urgent" : "Normal", 
         ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"), 
         RecordStatus = (r.RecordStatus=="NCF"? "Not Confirmed": "Approved"), 
         RequisitionFromName = (rf==null? null: rf.StoreName), 
         RequisitionToName = (rt == null ? null : rt.StoreName) 
        }); 

Прежде всего я снял ToList(), который ничего не делает, но выполняет запрос, который уже сделано когда я вызвал AsEnumerable(). Нет точек для выполнения одного и того же запроса дважды. Также мои пользовательские вызовы методов в блоке выбора также играли важную роль, замедляя работу. Я попробовал уменьшить вызовы метода, а скорее использовал соединение, где это возможно. Это делает вещи довольно быстрыми. Спасибо вам всем.

+0

Вы все еще смешиваете DAO с логикой дисплея. ВСЕ ваш код в избранном должен быть в вашем интерфейсе! Это действительно плохой дизайн программного обеспечения. – Aron

+0

@Jain, вам нужно задать себе вопрос - не волнуйтесь о загрузке какого-то сумасшедшего количества записей каждый раз, когда я запускаю этот запрос? Если да, этот подход не решит вашу проблему в любом случае. Если вы не беспокоитесь об этом, вы можете пойти с '.AsEnumerable'. – Alexey

+0

@ Арон, я знаю. Но проект уже разработан таким образом, поэтому у меня очень мало вариантов. Но определенно мы постараемся улучшить ситуацию в будущем. –

1

AsEnumerable() будет медленнее, если добавить любые элементы запроса после него.

Даже если AsEnumerable() не выполняет запрос непосредственно, применяя where или orderbyпослеAsEnumerable() означает, что Sql получит все элементы, а затем применить фильтрацию и заказа к коллекции в памяти ,

Короче:

  • Без AsEnumerable() = фильтрации и упорядочения сделано в SQL
  • С AsEnumerable(), а затем Where или orderby = применяется ко всей коллекции принесли в память.

Вы можете запускать только определенные пользователем функции в коллекции в памяти (поскольку ваш Linq to SQL не сможет интерпретировать ваши функции в SQL-коде). Таким образом, ваш второй фрагмент кода (без AsEnumerable()), вероятно, лучше всего.

Единственный альтернативный вариант - применить свои пользовательские функции в самом SQL.

+1

AsEnumerable() выводит всю запись в память, а затем применяет фильтры, поэтому мы можем использовать определенную пользователем функцию в блоке выбора без использования AsEnumerable(), используя любой пользовательский вызов метода внутри блок select выдает ошибку. –

+1

@ Джейн, вы абсолютно правы, мои извинения, 'AsEnumerable()' не выполняет запрос, приводящий результирующий набор в память, однако любые последующие утверждения или применяемые фильтры будут выполняться с набором результатов в памяти. Я соответствующим образом обновил ответ. –

3

Похоже, вы смешиваете эти два интерфейса как две совершенно разные вещи. Фактически, IQueryable унаследован от IEnumerable, поэтому все, что сработало для вас с последним, будет работать и с бывшим, поэтому не нужно использовать AsEnumerable.

За кулисами, хотя эти интерфейсы реализованы совсем по-другому: IEnumerable будет обрабатывать вашу коллекцию в памяти, а IQueryable передаст запрос базовому поставщику данных. Вы можете себе представить, что если таблица базы данных содержит миллионы записей и вы пытаетесь ее отсортировать, сервер БД может сделать это очень быстро (используя индексы), так что Queryable будет сиять. Для IEnumerable все данные должны быть загружены в память вашего компьютера и отсортированы там.

Для более длительного ответа поиска "Разница IEnumerable IQueryable" на SO, вы увидите большое количество деталей:

Random Link 1 Random Link 2

Update: Если убрать вызов .ToList из вашего второй пример, то результат не будет автоматически загружен в память. На этом этапе вам нужно решить, какие элементы вы хотите хранить в памяти, и вызывать их функции только для них.

var Data = (from r in _context.PRD_ChemProdReq 
      orderby r.RequisitionNo descending 
      select new PRDChemProdReq 
      { 
       // do your initialization 
      }); 

var subsetOfData = Data.Take(100).ToList(); // Now it's loaded to memory 
foreach (var item in subsetOfData) 
{ 
    item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory); 
    item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType); 
    item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy"); 
    item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus); 
    item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom); 
    item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo); 
} 

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

+0

Я могу понять ваши вопросы, но какой должен быть правильный способ сделать что-то быстрее в моем случае, если я не использую AsEnumerable(), я не могу использовать пользовательский вызов метода в блоке выбора, поэтому мне нужно перечислить позже, и если я использую AsEnumerable(), все записи поступают в память, что тоже не очень хорошо. –

+0

@Jain Техника, которая обычно используется, заключается в том, что вы делаете «выбор» необходимых вам полей, а затем в «foreach» вы «форматируете» их. Это то, что вы делаете во втором примере. – xanatos

+0

@xanatos Итак, вы говорите, что лучше, чем использовать .AsEnumerable(), правильно? Я постараюсь следовать ему. Благодарю. –

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