2010-09-28 12 views
1

J'utilise Castle ActiveRecord et NHibernate.Trouver la ligne avec les relations correspondantes en utilisant HQL

J'ai une classe instance qui a beaucoup à plusieurs avec une classe Component. Je voudrais trouver l'instance qui est liée à un ensemble spécifique de composants. Est-ce possible dans HQL (ou quoi que ce soit d'autre dans NHibernate)?

La version LINQ de cette fonction serait:

public Instance find(IEnumerable<Component> passed_components) 
{ 
    return Instance.Queryable.Single(i => passed_components.All(x => i.Components.Contains(x))); 
} 

Bien sûr, la mise en œuvre de LINQ NHibernate ne peut pas gérer cela.

Je peux écrire le HQL de le faire pour l'un des composants:

Instance.FindOne(new DetachedQuery("from Instance i where :comp in elements(i.Components)").SetParameter("comp", passed_components.First())); 

Mais il semble que dans compare un seul élément à un ensemble, il ne peut pas comparer un ensemble à un ensemble .

EDIT:

C'est le meilleur que je pouvais faire:

IQueryable<Instance> q = Queryable; 
foreach(var c in components) { 
    q = q.Where(i => i.Components.Contains(c)); 
} 

Mais ce qui est très inefficace. Il ajoute un sous-objet à la requête SQL pour chaque clause where. Une sous-sélection anecissarily longue à cela. Il joint la table Instance, la table de jointure Instance/Component et la table Component. Il n'a besoin que de la table de jointure Instance/Component.

En raison de la nature de mes données, je vais mettre en œuvre une solution hybride. Affinez les instances dans la requête, puis utilisez linq pour les objets pour obtenir le bon, si nécessaire. Le code ressemble à ceci:

IQueryable<Instance> q = Queryable; 
foreach(var c in components.Take(2)) { 
    q = q.Where(i => i.Components.Contains(c)); 
} 

var result = q.ToArray(); 
if(result.Length > 1) { 
    return result.SingleOrDefault(i => !components.Except(i.Components).Any()); 
} 
else return result.FirstOrDefault(); 

Quelqu'un at-il une meilleure façon?

Répondre

1

Utilisation du fournisseur NHibernate.Linq, ce qui suit devrait fonctionner:

var passed_components = new List<Component>(); 
var instance = session.Linq<Instance>() 
         .Where(i => !passed_components.Except(i.Components).Any()) 
         .SingleOrDefault(); 

Vous pouvez télécharger le fournisseur here et lire davantage here et here.

+0

Etes-vous sûr que cela fonctionne? Je suis assez sûr que Contains ne prend qu'un seul objet, pas une liste. ActiveRecord est livré avec un fournisseur Linq qui ne le permet pas. J'ai aussi vérifié NHibernate.Linq dans nhcontrib, ça ne marche pas non plus. – oillio

+0

@oillio: Mon erreur, je m'excuse. Cependant, j'ai modifié le post pour le code pour comparer si une liste en contient une autre, à la suite de cette http://bit.ly/aQSE31. Cependant, je ne suis pas à 100% ça marchera avec NHibernate.Linq. – rebelliard

+0

Cette version est beaucoup mieux que mon code linq ci-dessus. Malheureusement, cela ne fonctionnera pas non plus avec NHibernate. – oillio