Подтвердить что ты не робот

Эквивалентность синтаксиса запроса и метода (лямбда) предложения Join with Where

Мой упрощенный LINQ Join plus Where из двух таблиц выглядит следующим образом:

var join = context.Foo
  .Join(context.Bar,
    foo => new { foo.Year, foo.Month },
    bar => new { bar.Year, bar.Month },
    (foo, bar) => new { foo.Name, bar.Owner, foo.Year })
  .Where(anon => anon.Year == 2015).ToList();

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

var joinQuery = from foo in context.Foo
                join bar in context.Bar
                on new { foo.Year, foo.Month } equals new { bar.Year, bar.Month }
                where foo.Year == 2015
                select new { foo.Name, bar.Owner };
var join = joinQuery.ToList();

Одна разница, которая возникает у меня, и что я задаюсь вопросом, это порядок команд. В соединении лямбда-синтаксиса я добавляю свойство foo.Year к моему типу анонимного возврата, чтобы я мог фильтровать его, а в другом запросе я все еще могу использовать foobar, если захочу) в Where. Мне не нужно добавлять поле foo.Year к моему типу возврата здесь, если я не хочу или нуждаюсь.

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

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

.Select(anon => new { /* the properties I want */ })

Но разве это не просто добавляет "еще одно" создание анонимного типа по сравнению со вторым утверждением, или я ошибаюсь здесь?

Вкратце: какой эквивалентный синтаксис Join для второго оператора? Или первый, плюс добавленный Select действительно эквивалентный, т.е. Внутри joinQuery создает тот же код?

4b9b3361

Ответ 1

В общем случае вы не всегда можете преобразовать между синтаксисом понимания запроса и синтаксисом лямбда точно так же, как это делает компилятор. Это связано с использованием прозрачных идентификаторов . Но вы можете обойти это и создать семантически эквивалентные лямбда-выражения. И это то, что делает ReSharper.

В любом случае, в вашем случае вы можете просто добавить:

.Select(anon => new { /* the properties I want */ })

Это создаст анонимный тип для каждой строки, но не будет "еще одним", поэтому не беспокойтесь об этом: выражение преобразуется в SQL, поэтому new { foo.Year, foo.Month } операторы в join на самом деле не создают экземпляры этих объектов, они просто преобразуются в SQL. Только последний выбор будет использоваться для списка SQL SELECT и для гидратации объекта после получения строк.

Ответ 2

Но разве это не просто добавляет "еще одно" создание анонимного типа по сравнению со вторым утверждением, или я ошибаюсь здесь?

Как показано в ответе dotctor: это то, что делает компилятор, когда вы используете синтаксис понимания в этом случае. Включив год в свой анонимный тип, вы немного уменьшите накладные расходы.

Однако:

  • Анонимные типы очень легкие: использование дженериков, и что генерические экземпляры, созданные через ссылочные типы, совместно используют реализацию, означает, что кода мало (посмотрите на сборку в декомпиляторе).
  • Пока создано множество экземпляров, в большинстве случаев они будут очищены в генерации 0, поскольку они будут выпущены почти сразу.
  • У компилятора С# и оптимизаторов JIT есть много возможностей для работы здесь. Вполне возможны быстрые клавиши (но вам нужно будет прочитать сборку x86/x64 из работающего процесса, чтобы увидеть).

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

Ответ 3

Другой альтернативой было бы переместить метод where до метода join, а затем оставить год вне анонимного класса:

var join = context.Foo
    .Where(foo => foo.Year == 2015)
    .Join(context.Bar,
        foo => new { foo.Year, foo.Month },
        bar => new { bar.Year, bar.Month },
        (foo, bar) => new { foo.Name, bar.Owner })
    .ToList();

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

Ответ 4

для Короче: часть вашего вопроса: ответ да.
здесь результат resharpers

var joinQuery = context.Foo.Join(context.Bar, foo => new
{
    foo.Year,
    foo.Month
}, bar => new
{
    bar.Year,
    bar.Month
}, (foo, bar) => new
{
    foo,
    bar
}).Where(@t => @t.foo.Year == 2015).Select(@t => new
{
    @t.foo.Name,
    @t.bar.Owner
});