2010-01-25 3 views
2

Я получаю коллекцию объектов Entity Framework из таблицы продуктов, называемой oProducts. Я просматриваю коллекцию и устанавливаю поле Quantity из отдельной функции. Прежде чем я выложу отображение объектов в HTML, я хочу отсортировать их по количеству. Итак, я использую LINQ для создания новой коллекции и пытаюсь заказать из коллекции объектов, которую я только что изменил. Измененные значения, безусловно, находятся в коллекции объектов и выводятся правильно, но порядок сортировки не работает в модифицированном поле. Кто-нибудь знает, что я пропускаю, или лучший способ попытаться это сделать?Order By по модифицированному объекту Entity Framework

Dim oProducts = From p in ctx.Products _ 
       Where p.Active = True _ 
       Select p 

For Each prod in oProducts 
    prod.Quantity = GetQuantity(prod.ProductID) 
Next 

Dim oOrderedProducts = From p in oProducts _ 
         Order By p.Quantity Ascending _ 
         Select p 

For Each prod in oOrderedProducts 
    Response.Write(prod.Quantity.ToString()) 
    '*** The Quantities are stored in the collection properly but it doesn't order by the Quantity field. 
Next 

ответ

4

Там несколько вещей, которые нужно помнить:

  1. Ведение From p in ctx.Products _ воли всегда выберите из базы данных.
  2. LINQ ленив. Он не будет выполнять выбор базы данных до тех пор, пока вы не выполните итерацию, и если вы повторите итерацию дважды, она будет дважды выбирать базу данных.
  3. Как данные, считываемые из базы данных, объединены с данными, уже находящимися в ObjectContext, будут различаться в зависимости от MergeOption запроса, который вы выполняете. Но всегда будет после выполняется SQL.

С этими идеями в виду, давайте рассмотрим код:

Этот бит (1) создает IQueryable<Product>, который может быть использован в качестве основы для итерации или определения других запросов. Обратите внимание, что он фактически не выполняет запрос базы данных.

Dim oProducts = From p in ctx.Products _ // (1) 
       Where p.Active = True _ 
       Select p 

Следующий бит (2) выполняет итерацию IQueryable, определенную выше.

For Each prod in oProducts       // (2) 
    prod.Quantity = GetQuantity(prod.ProductID) 
Next 

Выполнение этого запроса вызывает выбор из базы данных. Внутри цикла вы мутируете возвращаемый объект. В LINQ to Objects это было бы никакого эффекта вообще,, потому что в следующий раз, когда вы повторили IQueryable, он будет выполнен заново. Однако объекты Entity являются особыми. Мутирование их отмечает, что сущность изменена в контексте. Вызов SaveChanges() позже сохранит эти изменения в базе данных.

Важно отметить, однако, что вы не изменили IQueryable переменной oProductsна всех, делая это. Он по-прежнему является ссылкой на запрос, который будет выполняться каждый раз, когда он повторяется. Это не список. Если вы хотите получить список, вам необходимо позвонить ToList() или ToArray().

Следующий запрос (3) создает новый IQueryable на основе оригинала oProducts.При повторе это приведет к созданию нового SQL, который делает то же самое, что и исходный запрос, за исключением добавленного вами заказа. Опять же, oProducts - это запрос, а не список.

Dim oOrderedProducts = From p in oProducts _     // 3 
         Order By p.Quantity Ascending _ 
         Select p 

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

For Each prod in oOrderedProducts       
    Response.Write(prod.Quantity.ToString()) 
    '*** The Quantities are stored in the collection properly but it doesn't order by the Quantity field. 
Next 
0

Как насчет этого?

Dim oProducts = From p in ctx.Products _ 
       Where p.Active = True _ 
       Select p 

For Each prod in oProducts 
    prod.Quantity = GetQuantity(prod.ProductID) 
Next 

Dim oOrderedProducts = oProducts.OrderBy(Function(p) p.Quantity) 
+0

Этот код приведет к ошибке выполнения, поскольку LINQ to Entities не сможет перевести 'GetQuantity'. Один из способов - сначала вызвать «AsEnumerable()». –

+0

Да, не удалось зарегистрировать L2E; думал Линк-К-Sql. Теперь, что произойдет, если вы вызываете ToList() или ToArray при создании oProducts? – Jay

+1

Тогда это сработает. * Почему * это сработало, но сложнее, чем перевод на SQL; Я объясняю это в своем ответе. –

1

Вам не нужно использовать 3 последовательных выражений LINQ, объединить их в один, используя проекцию, либо в анонимный тип (как я здесь) или реальный. Это ниже, должны работать

var oProducts = From p in ctx.Products 
       Where p.Active == True 
       orderby p.Quantity Ascending 
       select new 
       { 
        Quantity = GetQuantity(p.ProductID) 
        // ... assign other properties too 
       } 

foreach (var prod in oProducts) 
{ 
    // do your output 
} 
+0

У вас такая же проблема, как у Джея. LINQ to Entities не будет знать, как перевести 'GetQuantity' в SQL, так что это приведет к ошибке выполнения. –

+0

ahhh вы правы - моя ошибка, да, вам нужно будет переосмыслить, как писать LINQ где/select, чтобы он мог успешно ударить db, я упустил из виду тот факт, что orderby был на той же переменной, что и я пытался установить в проекции – Bobby