LINQ Exception: «Запросы с локальными коллекциями не поддерживаются». но не используя местную коллекцию - PullRequest
2 голосов
/ 11 декабря 2010

Используя таблицу Products базы данных AdventureWorks в качестве примера, я создал DBML и расширил свойства DataContext, добавив новое свойство:

partial class AdventureWorksDataContext
{
  public IQueryable<Product> FinishedProducts
  {
    get { return Products.Where(p => p.FinishedGoodsFlag); }
  }
}

Свойство Products является частью сгенерированного DataContext ивсе, что я сделал, это добавил Where из таблицы , чтобы он возвращал IQueryable.

Теперь проблема возникает, когда я пытаюсь выполнить запрос следующим образом (тупой пример, который должен показать проблему).):

var queryFinishedProducts = datacontext.FinishedProducts.Where(fp => fp.ProductID == datacontext.FinishedProducts.Max(p => p.ProductID));

Повторение этого запроса приводит к исключению «Запросы с локальными коллекциями не поддерживаются».Я не понимаю, почему это вызвало бы такую ​​ошибку, если не используются локальные коллекции.Если я запускаю его на обычной таблице продуктов (которая является Table<Product>):

var queryProducts = datacontext.FinishedProducts.Where(fp => fp.ProductID == datacontext.Products.Max(p => p.ProductID));

... она работает нормально.Единственное отличие состоит в том, что я добавил Where к Table<Product> и вернул его как IQueryable<Product>.

У кого-нибудь есть идеи?

Ответы [ 2 ]

0 голосов
/ 11 декабря 2010

Мне удалось воспроизвести это поведение.Вот еще несколько типов, которые нужно посмотреть в отражателе:

System.Data.Linq.SqlClient.SqlBinder.Visitor
System.Data.Linq.SqlClient.SqlVisitor

На основе этой трассировки стека.

at System.Data.Linq.SqlClient.SqlBinder.Visitor.ConvertToFetchedSequence(SqlNode node)
at ..SqlBinder.Visitor.VisitAlias(SqlAlias a)
at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
at ..SqlVisitor.VisitSource(SqlSource source)
at ..SqlBinder.Visitor.VisitSelect(SqlSelect select)
at ..SqlVisitor.Visit(SqlNode node)
at ..SqlBinder.Visitor.VisitAlias(SqlAlias a)
at ..SqlVisitor.Visit(SqlNode node)
at ..SqlVisitor.VisitSource(SqlSource source)
at ..SqlBinder.Visitor.VisitSelect(SqlSelect select)
at ..SqlVisitor.Visit(SqlNode node)
at ..SqlVisitor.VisitSequence(SqlSelect sel)
at ..SqlVisitor.VisitScalarSubSelect(SqlSubSelect ss)
at ..SqlVisitor.VisitSubSelect(SqlSubSelect ss)
at ..SqlBinder.Visitor.VisitSubSelect(SqlSubSelect ss)

Интересно, почему свойство типа Table<T> трактуется иначе, чемсвойство типа IQueryable<T> или даже ITable<T>.Реализация свойства не имеет значения, тип возвращаемого значения имеет значение.


Свойство типа IQueryable<T> определенно обрабатывается иначе, чем локальная переменная типа IQueryable<T>

IQueryable<Customer> query1 =
  myDC.Customers.Where(c => c.ID == myDC.CoolCustomers.Max(c2 => c2.ID));

IQueryable<Customer> query2 =
  myDC.Customers.Where(c => c.ID == myDC.Customers.Where(c2 => c2.Flag).Max(c2 => c2.ID));

IQueryable<Customer> subQuery = myDC.CoolCustomers;
IQueryable<Customer> query3 =
  myDC.Customers.Where(c => c.ID == subQuery.Max(c2 => c2.ID));

query1 демонстрирует исходное поведение (исключение, локальные последовательности).

query2 генерирует этот sql - это не идеально.

SELECT [t0].[ID], [t0].[Flag]
FROM [Customer] AS [t0]
OUTER APPLY (
    SELECT MAX([t1].[ID]) AS [value]
    FROM [Customer] AS [t1]
    WHERE [t1].[Flag] = 1
    ) AS [t2]
WHERE [t0].[ID] = [t2].[value]

query3 слишком охотно выдает подзапрос во время перевода запроса, затем выполняет вторую обратную передачу для основного запроса.

0 голосов
/ 11 декабря 2010

Вот один подход, который должен работать:

var max = datacontext.FinishedProducts.Max(p => p.ProductID);
var queryFinishedProducts = datacontext.FinishedProducts
                                       .Where(fp => fp.ProductID == max);

Или, предположив, что ProductID уникален, попробуйте переписать ваш запрос следующим образом:

var queryProducts = datacontext.FinishedProducts
                               .OrderByDescending(p => p.ProductID)
                               .First();
...