Как включить выражение InvocationExpression, которое оценивается только один раз в дереве выражений для linq to sql? - PullRequest
2 голосов
/ 24 февраля 2009

Я пытаюсь вручную объединить деревья выражений для достижения уровня модульности, который, кажется, намекает на меня, используя стандартные операторы linq. Код по существу создает дерево выражений, которое использует одно выражение, чтобы решить, какое из двух других выражений вызвать. Для одного из других выражений требуется дополнительный параметр, который сам получается с использованием другого выражения. Этот параметр используется для получения нескольких значений, но для каждого доступа выражение, которое извлекает параметр, повторяется. Я включил код и вывод, чтобы лучше объяснить:

public void Test() {
    var parameters = ProjectionOne.Parameters;
    Expression<Func<Foo, bool>> isType = f => f.TypeId == 1;
    Expression<Func<Foo, Satellite>> satSelector = f => f.Satellites.Single();
    var satelliteSelector = Expression.Invoke(satSelector, parameters[0]);
    var test = Expression.Lambda<Func<Foo, Bar>>(
        Expression.Condition(
            Expression.Invoke(isType, parameters[0]),
            Expression.Invoke(ProjectionOne, parameters[0]),
            Expression.Invoke(ProjectionTwo, parameters[0], satelliteSelector)), parameters);
}

public Expression<Func<Foo, Bar>> ProjectionOne {
    get {
        return foo => new Bar() {
            Id = foo.Id
        };
    }
}

public Expression<Func<Foo, Satellite, Bar>> ProjectionTwo {
    get {
        return (foo, sat) => new Bar() {
            Id = foo.Id,
            Start = sat.Start,
            End = sat.End
        };
    }
}

Когда я запускаю этот запрос к базе данных, SQL выдается следующим образом:

SELECT [t0].[value], [t0].[value2] AS [Start], [t0].[value3] AS [End], [t0].[Id] AS [Id]
FROM (
    SELECT 
        (CASE 
            WHEN [t0].[TypeId] = @p0 THEN 1
            WHEN NOT ([t0].[TypeId] = @p0) THEN 0
            ELSE NULL
         END) AS [value], (
        SELECT [t2].[Start]
        FROM [dbo].[Satellite] AS [t2]
        WHERE [t2].[Id] = [t0].[Id]
        ) AS [value2], (
        SELECT [t2].[End]
        FROM [dbo].[Satellite] AS [t2]
        WHERE [t2].[Id] = [t0].[Id]
        ) AS [value3], [t0].[Id]
    FROM [dbo].[Foo] ) AS [t0]

Проблема в том, что дубликат суб-выбора. Один получает значение «Старт», а другой - значение «Конец». Было бы намного лучше, если бы они оба были получены из одного суб-выбора. Как я могу изменить конструкцию дерева выражений для обеспечения этого? Кроме того, я понимаю, что существуют более простые способы выполнить этот запрос, однако он является частью большой структуры, не показанной здесь, которая может быть достигнута только путем возможности вручную собрать деревья выражений из большого набора многократно используемых выражений.

1 Ответ

1 голос
/ 24 февраля 2009

Я подозреваю, что вы уже сделали все, что могли (так как вы уже используете проекцию, двигатель выбирает для выражения в качестве отдельных подвыборов).

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

Профилировали ли вы запрос и сравнивали ли вы с вашим предпочтительным макетом? Возможно, , что профиль идентичен (т. Е. Оптимизатор TSQL имел дело с ним). В этом случае не нужно слишком беспокоиться.

...