2009-11-30 3 views
2

Je travaille avec un client qui souhaite combiner LINQ à SQL avec sa couche DAL interne. En fin de compte, ils veulent être en mesure d'interroger leur couche en utilisant la syntaxe LINQ typique. Le point où cela devient délicat, c'est qu'ils construisent leurs requêtes de manière dynamique. Donc finalement ce que je veux, c'est pouvoir prendre une requête LINQ, la démonter et être capable d'inspecter les pièces pour sortir les bons objets, mais je ne veux pas vraiment construire une pièce pour traduire l'expression 'where' en SQL. Est-ce quelque chose que je peux juste générer en utilisant le code Microsoft? Ou y a-t-il un moyen plus facile de le faire?Extraction de la clause WHERE de LINQ to SQL

+0

exemple Ajouté par commentaire –

Répondre

4

(vous juste milieu LINQ, pas vraiment LINQ to SQL)

Bien sûr, vous pouvez le faire - mais il est des quantités massives de travail. Here's how; Je recommande "ne pas". Vous pouvez également regarder le code source pour DbLinq - voir comment ils le font.

Si vous voulez justeWhere, il est un peu plus facile - mais dès que vous commencez à obtenir des jointures, groupements, etc - il sera très difficile à faire.


Voici justeWhere support sur un implemention LINQ personnalisé (pas un fournisseur entièrement interrogeable, mais assez pour obtenir LINQ avec Where de travail):

using System; 
using System.Collections.Generic; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace YourLibrary 
{ 
    public static class MyLinq 
    { 
     public static IEnumerable<T> Where<T>(
      this IMyDal<T> dal, 
      Expression<Func<T, bool>> predicate) 
     { 
      BinaryExpression be = predicate.Body as BinaryExpression; 
      var me = be.Left as MemberExpression; 
      if(me == null) throw new InvalidOperationException("don't be silly"); 
      if(me.Expression != predicate.Parameters[0]) throw new InvalidOperationException("direct properties only, please!"); 
      string member = me.Member.Name; 
      object value; 
      switch (be.Right.NodeType) 
      { 
       case ExpressionType.Constant: 
        value = ((ConstantExpression)be.Right).Value; 
        break; 
       case ExpressionType.MemberAccess: 
        var constMemberAccess = ((MemberExpression)be.Right); 
        var capture = ((ConstantExpression)constMemberAccess.Expression).Value; 
        switch (constMemberAccess.Member.MemberType) 
        { 
         case MemberTypes.Field: 
          value = ((FieldInfo)constMemberAccess.Member).GetValue(capture); 
          break; 
         case MemberTypes.Property: 
          value = ((PropertyInfo)constMemberAccess.Member).GetValue(capture, null); 
          break; 
         default: 
          throw new InvalidOperationException("simple captures only, please"); 
        } 
        break; 
       default: 
        throw new InvalidOperationException("more complexity"); 
      } 
      return dal.Find(member, value); 
     } 
    } 
    public interface IMyDal<T> 
    { 
     IEnumerable<T> Find(string member, object value); 
    } 
} 
namespace MyCode 
{ 
    using YourLibrary; 
    static class Program 
    { 
     class Customer { 
      public string Name { get; set; } 
      public int Id { get; set; } 

     } 
     class CustomerDal : IMyDal<Customer> 
     { 
      public IEnumerable<Customer> Find(string member, object value) 
      { 
       Console.WriteLine("Your code here: " + member + " = " + value); 
       return new Customer[0]; 
      } 
     } 
     static void Main() 
     { 
      var dal = new CustomerDal(); 
      var qry = from cust in dal 
         where cust.Name == "abc" 
         select cust; 

      int id = int.Parse("123"); 
      var qry2 = from cust in dal 
         where cust.Id == id // capture 
         select cust; 


     } 
    } 
} 
+0

Je ne veux juste où. Je suis d'accord que le regroupement et les jointures sont hors de portée, et la société ne veut pas vraiment utiliser les données de cette façon de toute façon. Ils sont énormes pour extraire des objets complets de la base de données. En fin de compte, je pense que ce soit comme IEnumerable Résultats = de la catégorie dans CompanyDal.Categories où Category.ID = 5 select Catégorie Je voudrais être en mesure de lancer une exception si elle ne correspond pas modèle simple, puis construire ma clause SQL où hors de cette expression où. Et finalement, s'ils décident de ne pas le vouloir, c'est bien, tant que c'est possible finalement. Merci. – Hounshell

+0

OK; Je vais ajouter un exemple qui fait juste cela, alors. –

0

Je viens de lire un article intéressant sur Expression Trees, LINQ to SQL les utilise pour traduire la requête en SQL et l'envoyer sur le réseau.

Peut-être que c'est quelque chose que vous pourriez utiliser?

1

Techniquement si votre DAL expose IQueryable<T> Au lieu de IEnumerable<T>, vous pouvez également mettre en œuvre un IQueryProvider et faire exactement ce que vous décrivez. Cependant, ce n'est pas pour les faibles de cœur. Mais si vous exposez les tables LINQ to SQL elles-mêmes dans le DAL, elles feront exactement cela pour vous. Il y a un (grand) risque puisque vous manipulerez le contrôle total du code client sur la façon d'exprimer les requêtes SQL, et le résultat habituel est une requête complexe qui joint tout et qui pique la pagination avec un temps d'exécution moins que spectaculaire performance.

Je pense que vous devriez examiner attentivement ce qui est réellement nécessaire de la DAL et exposer seulement cela.

0

Juste un peu cependant. Je connais un support de langage qui construit une chaîne qui peut être exécutée dans le code lui-même. Je n'ai jamais essayé avec .Net, mais c'est commun dans les langages fonctionnels comme LISP. Depuis .Net support lambdas, peut-être que c'est possible. Puisque F # arrive bientôt sur .Net, peut-être que ce sera possible si ce n'est pas le cas maintenant. Ce que j'essaie de dire, c'est que si vous pouvez le faire, vous pouvez peut-être créer cette chaîne qui sera utilisée comme instruction LINQ, puis l'exécuter. Puisque c'est une chaîne, il sera possible d'analyser la chaîne et d'obtenir l'information que vous voulez.

0

à quelqu'un d'autre avec la même question là-bas.Extraire la clause where de LINQ-to-SQL n'est pas aussi simple, comme on aurait pu l'espérer. De plus, faire cela par lui-même n'a probablement aucun sens. Il y a quelques options, selon les exigences - soit l'attraper à partir de la chaîne générée, mais alors il contiendrait des références de paramètres et des mappings de propriété d'objet qui devraient également être résolus, donc ceux-ci devraient également être retirés du fournisseur d'origine en quelque sorte, sinon ce serait inutile. Un autre - serait de trouver un fournisseur modulaire qui peut le faire, ainsi que rendre les mappages de membres facilement accessibles, mais encore une fois, sans le reste de la requête, je vois peu d'utilité à le faire, car la clause where référencer table/alias de colonnes de l'instruction select.

J'ai eu une tâche similaire d'écrire un fournisseur complet pour un ORM/DAL personnalisé il y a quelques années. Bien que cela soit considéré comme la chose la plus complexe sur laquelle j'ai travaillé, étant un développeur d'expérience, je peux dire que ce n'est pas aussi grave, comme le prétendent certaines personnes une fois que vous avez compris les concepts qui sont à la base. Certaines solutions que j'ai vues vont dans le mauvais sens, ajoutent des fonctionnalités redondantes et ont des problèmes d'adressage de code supplémentaires introduits par la logique sous-jacente. Par exemple. l'étape/module "optimisation" qui tente de recalculer le SQL imbriqué gonflé produit par l'analyseur principal. Si ce dernier était conçu de telle sorte qu'il produirait un SQL propre dès le début, aucune phase de nettoyage ne serait nécessaire. J'ai vu des fournisseurs qui créent un nouveau niveau d'imbrication pour chaque appel d'invocation. C'est une mauvaise stratégie. En décomposant une requête en trois/quatre parties principales - select, from, where et orderby, qui sont construites individuellement lors de la visite de l'arbre, ce problème est complètement évité. J'ai développé un objet-à-données (aka LINQ-to-SQL) fourni basé sur ces principes pour un ORM/DAL personnalisé et il produit un bon, propre SQL, avec une excellente performance, comme chaque déclaration est compilée à IL et mis en cache.

Pour tous ceux qui cherchent à faire quelque chose de similaire, veuillez consulter mes articles qui incluent un exemple de projet avec une implémentation tutorial/barebones qui permet de voir facilement comment cela fonctionne. Inclus aussi est la solution complète:

Questions connexes