2009-02-24 7 views
2

J'essaye de combiner manuellement des arbres d'expression pour accomplir un niveau de modularité qui semble me faire allusion en utilisant les opérateurs linq standard. Le code crée essentiellement un arbre d'expression qui utilise une expression pour décider laquelle des deux autres expressions à appeler. Une des autres expressions nécessite un paramètre supplémentaire qui est lui-même obtenu en utilisant une autre expression. Ce paramètre est utilisé pour obtenir plusieurs valeurs, mais pour chaque accès, l'expression qui récupère le paramètre est répétée. J'ai inclus le code et la sortie pour mieux expliquer:Comment inclure une InvocationExpression qui n'est évaluée qu'une seule fois dans une arborescence d'expression pour linq to sql?

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 
     }; 
    } 
} 

Quand je lance cette requête sur une base de données SQL est produit comme suit:

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] 

Le problème est le double sous choisit. L'un récupère la valeur 'Start', tandis que l'autre récupère la valeur 'End'. Ce serait beaucoup mieux s'ils étaient tous les deux récupérés à partir d'un seul sous-select. Comment puis-je modifier la construction de l'arbre d'expression pour l'appliquer? De plus, je comprends qu'il existe des façons plus simples d'exécuter cette requête, mais cela fait partie d'un cadre plus large qui ne peut être atteint qu'en étant capable d'assembler manuellement des arbres d'expression à partir d'un grand nombre d'expressions réutilisables.

Répondre

1

Je suppose que vous avez déjà fait tout ce que vous pouvez (puisque vous utilisez déjà une projection, que le moteur est en choisissant pour exprimer comme des sous-sélections individuelles). Les seules autres choses auxquelles je peux penser sont (par exemple) l'utilisation d'une UDF à valeur de table (via le contexte de données) pour obtenir le début/fin - voir si elle aplatit cela dans la requête.

Avez-vous profilé la requête et comparé à votre mise en page préférée? Il pourrait être bien que le profil soit identique (c'est-à-dire que l'optimiseur TSQL s'en est occupé). Dans ce cas, pas besoin d'être trop concerné.

+0

Merci pour la réponse Marc. J'avais essayé de le profiler et le chemin d'exécution de la requête montrait deux branches pour le double sélection, chacune utilisant 25%. Ma disposition préférée est de faire une jointure externe gauche avec 'Satellite' qui crée un chemin d'exécution beaucoup plus simple. Donc, la différence est de 4 index recherchés en cluster vs 3 – LaserJesus

+0

N'existe-t-il aucun moyen de restructurer l'expression pour qu'elle soit exprimée comme une jointure externe gauche? – LaserJesus

Questions connexes