2015-03-25 2 views
1

J'utilise PredicateBuilder pour créer des expressions réutilisables en tant que valeurs de retour d'objets. Par exemple:Tests automatisés pour la traduction d'expression

public interface ISurveyEligibilityCriteria 
{ 
    Expression<Func<Client, bool>> GetEligibilityExpression(); 
} 

Je veux avoir des tests automatisés qui déterminent si une expression particulière est traduisible dans T-SQL par Entity Framework (c.-à-qu'il ne jette pas un NotSupportedException alors que « l'exécution »). Je ne trouve rien sur Internet - est-ce possible (semble que ça devrait être)?

+0

N'êtes-vous pas en mesure de tester contre la mise en œuvre de l'interface? – Kami

Répondre

2

Vous pouvez créer une instruction LINQ contenant l'expression et de vérifier si elle peut se traduire sans réellement l'exécuter:

var connString = @"server=x;database=x"; 
using(var db = new MyContext(connString)) 
{ 
    // ToString() shows the generated SQL string. 
    var sql = db.Entities.Where(generatedExpression).ToString(); 
    Assert.IsTrue(sql.StartsWith("SELECT"); 
} 

Dans le Assert vous pouvez tester tout ce que vous vous attendez à faire partie du produit Chaîne SQL, mais bien sûr si l'expression ne peut pas être traduite, le test échouera car un NotSupportedException est lancé.


Vous pouvez envelopper ce en une méthode d'extension pratique:

public static class EntityFrameworkExtensions 
{ 
    public static void CompilePredicate<T>(this DbContext context, Expression<Func<T, bool>> predicate) 
     where T : class 
    { 
     context.Set<T>().Where(predicate).ToString(); 
    } 
} 

Puis dans votre test:

// act 
Action act =() => context.CompilePredicate(predicate); 

// assert 
act.ShouldNotThrow(); 
+0

Bravo :) n'a jamais pensé utiliser 'ToString()'. Encore est-il dommage que EF nécessite une instance 'DbContext' (peut-être cela va changer dans EF7) – AlexFoxGill

+0

@AlexG Sans un DbContext, il ne pourrait pas savoir quel serveur vous utilisez ... Oracle et MSSQL pourraient avoir un support différent pour différentes méthodes. – xanatos

+0

@xanatos vous avez peut-être raison, mais peut-être que l'utilisateur pourrait spécifier le moteur? J'imagine qu'en interne il y a une abstraction 'IQueryTranslator' - si nous avions accès au' MSSqlQueryTranslator' alors nous pourrions tester pour voir si la requête peut être générée, sans toucher notre 'DbContext' – AlexFoxGill

1

Une solution très simple est en cours d'exécution, il:

using (var context = ...) 
{ 
    // The query will return null, but will be executed. 
    context.Clients.Where(GetEligibilityExpression()) 
        .Where(() => false) 
        .SingleOrDefault(); 
} 

Dans les anciennes versions de EF (ou en utilisant ObjectContext) vous pourriez avoir essayé « manuellement » compilation de la requête avec CompiledQuery.Compile, mais ce n'est pas supporté avec DbContext .

+1

C'est une bonne idée mais convertir la requête en une chaîne sans l'exécuter est préférable pour la performance – AlexFoxGill