LINQ to SQL Выполнить без пропуска Причины нескольких операторов SQL - PullRequest
7 голосов
/ 13 мая 2009

У меня есть запрос LINQ to SQL:

from at in Context.Transaction
select new  {
    at.Amount,
    at.PostingDate,
    Details = 
        from tb in at.TransactionDetail
        select new {
            Amount = tb.Amount,
            Description = tb.Desc
        }
}

Это приводит к выполнению одного оператора SQL. Все хорошо.

Однако, если я попытаюсь вернуть из этого запроса известные типы, даже если они имеют ту же структуру, что и анонимные типы, я получу одну инструкцию SQL для верхнего уровня, а затем дополнительную инструкцию SQL для каждого «дочернего» набора .

Есть ли способ заставить LINQ to SQL выдать один оператор SQL и использовать известные типы?

РЕДАКТИРОВАТЬ: У меня должна быть другая проблема. Когда я подключил в LINQPad очень упрощенную (но все еще идиархальную) версию моего запроса и использовал только что созданные новые известные типы, содержащие всего 2 или 3 члена, я получил один оператор SQL. Я буду публиковать и обновлять, когда я знаю больше.

РЕДАКТИРОВАТЬ 2: Это, кажется, из-за ошибки в Take. См. Мой ответ ниже для деталей.

Ответы [ 3 ]

11 голосов
/ 15 мая 2009

Во-первых - некоторые причины для ошибки Take.

Если вы просто возьмете , переводчик запросов просто использует top. Top10 не даст правильного ответа, если кардинальность будет нарушена при присоединении к детской коллекции. Таким образом, переводчик запросов не включается в дочернюю коллекцию (вместо этого он запрашивает дочерние элементы).

Если вы Пропустите и возьмите , тогда переводчик запросов включит некоторую логику RowNumber над родительскими строками ... эти числовые числа позволяют ему взять 10 родителей, даже если это действительно 50 записей из-за каждого родителя имея 5 детей.

Если вы Пропустите (0) и возьмете , Пропуск удален переводчиком как неоперация - это как если бы вы никогда не сказали Пропустить.

Это будет сложный концептуальный скачок от того места, где вы находитесь (вызывая Skip and Take) к «простому обходному пути». Что нам нужно сделать - это заставить перевод произойти в тот момент, когда переводчик не может удалить Skip (0) как неоперацию. Нам нужно позвонить в «Пропустить» и предоставить пропущенный номер позже.

DataClasses1DataContext myDC = new DataClasses1DataContext();
  //setting up log so we can see what's going on
myDC.Log = Console.Out;

  //hierarchical query - not important
var query = myDC.Options.Select(option => new{
  ID = option.ParentID,
  Others = myDC.Options.Select(option2 => new{
    ID = option2.ParentID
  })
});
  //request translation of the query!  Important!
var compQuery = System.Data.Linq.CompiledQuery
  .Compile<DataClasses1DataContext, int, int, System.Collections.IEnumerable>
  ( (dc, skip, take) => query.Skip(skip).Take(take) );

  //now run the query and specify that 0 rows are to be skipped.
compQuery.Invoke(myDC, 0, 10);

Создает следующий запрос:

SELECT [t1].[ParentID], [t2].[ParentID] AS [ParentID2], (
    SELECT COUNT(*)
    FROM [dbo].[Option] AS [t3]
    ) AS [value]
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ID]) AS [ROW_NUMBER], [t0].[ParentID]
    FROM [dbo].[Option] AS [t0]
    ) AS [t1]
LEFT OUTER JOIN [dbo].[Option] AS [t2] ON 1=1 
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p1 + @p2
ORDER BY [t1].[ROW_NUMBER], [t2].[ID]
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [10]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

И вот где мы победим!

WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p1 + @p2
2 голосов
/ 13 мая 2009

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

Следующий результат в 1 операторе SQL:

query.Skip(1).Take(10).ToList();
query.ToList();

Тем не менее, следующее демонстрирует один SQL-оператор для каждой родительской строки.

query.Skip(0).Take(10).ToList();
query.Take(10).ToList();

Кто-нибудь может придумать какие-нибудь простые обходные пути для этого?

РЕДАКТИРОВАТЬ: Единственный обходной путь, который я нашел, - это проверить, нахожусь ли я на первой странице (IE Skip (0)), а затем сделать два вызова, один с Take (1), а другой с Пропустить (1). Взять (pageSize - 1) и добавить изменить списки вместе.

0 голосов
/ 13 мая 2009

У меня не было возможности попробовать это, но, учитывая, что анонимный тип не является частью LINQ, а скорее конструкцией C #, мне интересно, если бы вы могли использовать:

from at in Context.Transaction
select new KnownType(
    at.Amount,
    at.PostingDate,
    Details = 
        from tb in at.TransactionDetail
        select KnownSubType(
                Amount = tb.Amount,
                Description = tb.Desc
        )
}

Очевидно, что Details должна быть коллекцией IEnumerable.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...