Каковы правила перезаписи EF?

Когда синтаксическое выражение запроса LINQ написано с помощью DBContext, компилятор С# делает свою обычную магию преобразование синтаксиса запроса в синтаксис точек/расширений, используя его список 18 правил преобразования/срока перезаписи. Затем, когда запрос выполняется, EF применяет собственные внутренние правила перезаписи для создания выражения SQL.

Как и в статье, приведенной выше, я хотел бы иметь список правил перезаписи, применяемых EF. Где я могу это найти? Если я знаю правила перезаписи EF, я могу предсказать, что SQL EF будет генерировать для данного запроса, вместо того, чтобы ждать, пока время выполнения "увидит" сгенерированный SQL.

Например, рассмотрим следующие два запроса:

var result = from c in context.Customers
             from a in c.Accounts
             where c.ID > 2
             select a;

var result = from c in context.Customers
             where c.ID > 2
             from a in c.Accounts
             select a;

Когда компилятор С# заканчивает свои собственные правила перезаписи, вышеупомянутые запросы преобразуются в точечные обозначения со следующими соответствующими форматами:

SelectMany(...).Where(...).Select(...); //for the first query

Where(...).SelectMany(...); // for the second query

После этих преобразований EF начинает работать, применяя свои собственные правила перезаписи. Но EF имеет одну нормализованную форму для обоих вышеперечисленных запросов. Другими словами, оба запроса будут давать одинаковые результаты; либо запрос генерирует следующий оператор SQL:

SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Stat_ID] AS [Stat_ID]
    FROM  [dbo].[Customers] AS [Extent1]
    INNER JOIN [dbo].[Accounts] AS [Extent2] ON [Extent1].[ID] = [Extent2].[Customer_ID]
    WHERE [Extent2].[ID] > 2

Не зная правил перезаписи EF, я не мог этого предсказать. Мне просто пришлось выполнить и отладить код, чтобы сделать это наблюдение.

Итак, где я могу найти список правил перезаписи, применяемых EF?

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

Ответ 1

Преобразование выражения linq в SQL, к сожалению, очень сложно и, вероятно, не так легко разбивается на множество простых дискретных правил.

Например, такие вещи, как linq GroupBy и Join, не имеют тенденций сопоставлять один к одному с ключевыми словами SQL с тем же именем, что и в linq, которые они ведут себя немного иначе, как в SQL (linq GroupBy возвращает все элементы в группе, а не только агрегаты, а linq Join больше похож на SQL Server CROSS APPLY). Фактическое преобразование сильно зависит от того, что выбрано в конце запроса, что означает, что вам обычно приходится обрабатывать все выражение, а не простое сопоставление метода linq с ключевым словом SQL.

Хорошо читать статьи Matt Warrens о создании провайдера linq http://blogs.msdn.com/b/mattwar/archive/2008/11/18/linq-links.aspx. Возможно, это не совсем то, что делает EF, но дает вам представление о том, какие шаги используются для преобразования выражения linq в sql и связанных с этим сложностей.

Ответ 2

Все зависит от соединителя базы данных, например, если вы хотите видеть правила генерации для SQL-сервера, вы можете посмотреть источники EF. Но я не буду рекомендовать вам ретранслировать по этим правилам, они могут измениться в следующей версии. Я работаю с MySql и обновленным коннектором 3 раза, и каждый раз, когда я сталкиваюсь с проблемой, EF генерирует разные запросы и. Например, выражение:

    .Where(p => new List<int> {1, 2}.Contains(p.value))

в 6.5.4, сгенерированный как:

   WHERE 1 = `value` OR 2 = `value`

И в 6.7.4 сгенерировано как:

   WHERE `value` IN (1, 2)

P.S. если вы хотите увидеть правила генерации для MySql, проверьте Источники MySql.Data.Entities