NHibernate QueryOver Sous-Requête
J'ai regardé les questions similaires, mais ne peut pas trouver une explication simple. J'ai peut-être manqué, mais je promets que j'ai regardé. En fait, je ne peux même pas trouver la documentation sur l'autre que d'un seul post de blog qui occulte tout rapidement et suppose que vous êtes familier avec d'autres formes de NH.
Donné un plusieurs-à-plusieurs entre Program
et Topic
, où ce dernier est dans une hiérarchie de Topics
, je veux récupérer tous les Programs
pour un Topic
, éventuellement, y compris ses sous-rubriques. Depuis un programme peuvent être répertoriés sous plusieurs sous-thèmes d'un parent sujet, j'ai besoin d'utiliser une sous-requête ou de traiter avec avoir à utiliser distinct (et la simple approche de TransformUsing(Transformers.DistinctRootEntity)
n'a pas fonctionné).
SQL brut doit être quelque chose comme
SELECT ProgramId, Title, bar, baz, foo FROM Programs
WHERE ProgramId IN
(SELECT ProgramId from Program_Topics WHERE TopicId IN (1, 2, ...))
Les résultats sont exprimés dans un type de modèle pour le transfert à la vue. Ma première tentative a été ceci:
ProgramDTO pDTO = null;
/* topicIds is List<int> passed into function */
var query = Session.QueryOver<Program>()
.JoinQueryOver<Topic>(p => p.Topics)
.WhereRestrictionOn(pt => pt.Id).IsInG<int>(topicIds)
.TransformUsing(Transformers.DistinctRootEntity)
.SelectList(list => list
.Select(program => program.Id).WithAlias(() => pDTO.Id)
.Select(program => program.Title).WithAlias(() => pDTO.Title)
.Select(program => program.Location).WithAlias(() => pDTO.Location)
.Select(program => program.Description).WithAlias(() => pDTO.Description)
)
.TransformUsing(Transformers.AliasToBean(typeof(ProgramDTO)));
return query.List<ProgramDTO>();
Évidemment, cela fonctionne une jointure au lieu d'une sous-requête, mais je ne peux pas trouver un exemple de faire une sous-requête avec plusieurs-à-plusieurs de ce genre.
public class Program : Entity {
public virtual ISet<Topic> Topics { get; protected internal set; }
...
}
public class Topic : Entity {
public virtual ISet<Program> Programs { get; protected internal set; }
public virtual Topic ParentTopic { get; protected internal set; }
...
}
OriginalL'auteur Carl Bussema | 2012-02-14
Vous devez vous connecter pour publier un commentaire.
Vous avez besoin pour créer un dettachées requête contenant les Id et ensuite utiliser cette sous-requête avec la requête principale.
J'ai collé un exemple ici, donc vous aurez besoin d'avoir à remplacer la bits avec vos noms de classe etc.
D'abord la mise en place (vous pouvez ignorer ce bit):-
Maintenant la dettachées requête:-
Et le bit final est de mettre tout cela ensemble:-
Cela va créer la requête SQL suivante:-
J'espère que vous serez en mesure de suivre avec votre classe/les noms de propriété.
SELECT * FROM cmspage WHERE id IN (SELECT ... cmsPageId FROM cmsregioncontent WHERE regionid IN ( ... ))
à ce que vous avez là. La version donnée ici n'est vraiment pas très différent que de simplement faire une Jointure Interne.En fait, après vérification, NH est encore générer des Programmes de JOINTURE INTERNE ProgramsTopics INNER JOIN Sujets.
var topicSubQuery = QueryOver.Of<Topic>().WhereRestrictionOn(pt => pt.Id).IsIn(topicIds).Select(Projections.Distinct(Projections.Property<Program>(p => p.Id)));
var progQuery = Session.QueryOver<Program>().Where(p => p.Status != ProgramStatus.Archived).JoinQueryOver<Topic>(p => p.Topics).WithSubquery.WhereProperty(pt => pt.Id).In(topicSubQuery).SelectList(list => list /* ...snip... */) .TransformUsing(Transformers.AliasToBean(typeof(ProgramDTO)));
OriginalL'auteur Rippo
Bien, haché à ce un peu plus, et bien que je n'aime pas une partie des résultats, il fonctionne:
Ce produit
Cela peut être une limitation de NH, mais il n'y a pas besoin pour rejoindre le Programmes table dans la sous-requête. J'ai essayé d'écrire cela dans l'autre sens-qui est, de créer un
QueryOver.Of<Topic>()
, mais je ne pouvais pas comprendre comment sélectionner l'Id de programme à la fin -- sélectionnez était seulement de me donner la TopicIds, et même alors, la requête a été encore se joindre à tous les trois tables.Je ne sais pas si MS-SQL de l'optimiseur de requête d'éviter l'inutile adhérer ou pas, mais ce serait bien si nous n'avons pas besoin de compter sur elle.
Pour l'instant, cela fonctionne, et j'espère que quelqu'un d'autre a moins de maux de tête que j'ai fait en essayant de le comprendre.
Si vous avez la main-d'écrire une requête HQL vous donner un typage fort et de refactoring, ce qui est un avantage majeur pour QueryOver. Alors qu'il n'créer un supplément de le REJOINDRE, je m'attends à un optimiseur de requête SQL de voir que vous n'avez pas besoin de l'appoint de la table par la lecture de la clause de JOINTURE et de comprendre qu'il peut sélectionner le champ à la place et obtenir les mêmes résultats. Si rien d'autre, il sera probablement exécuter le OÙ d'abord et ensuite rejoindre sur un indice, de sorte qu'il sera rapide.
Je dois avouer que ma connaissance de l'exécution de code SQL de SQL Server et de ses possible et même probable optimisations sont rares, donc je préfère ne pas compter sur elle trop. Mais merci pour l'idée 🙂
Si vous pouvez venir avec une solution qui utilise QueryOver ou Linq-to-Nhibernate et de ne pas se retrouver avec le supplément de REJOINDRE, j'accepte cette réponse 🙂 Mais HQL est tout simplement pas une option pour moi.
OriginalL'auteur Carl Bussema