2009-03-01 6 views
4

J'essaie de construire une expression de critère nHibernate pour effectuer une recherche.Filtre sur les jointures dans nHibernate

Compte tenu du modèle de données suivantes:

  • Une opération peut avoir zéro ou plusieurs sessions.
  • Une opération peut être de plus de types d'opérations.

Je veux rechercher toutes les sessions en fonction des critères suivants:

  • (obligatoire) Lorsque l'opération drapeau IsActive est vrai, IsPublished drapeau est vrai

o (Facultatif) et lorsque les dates d'état/de fin de l'opération sont comprises dans la plage de dates spécifiée par l'utilisateur

o (Facultatif) et où la session regionId correspond à un ID utilisateur spécifié

o (en option) et où la division de la session correspond à un utilisateur identifiant spécifié

o (en option) et où les Operation.OperationTypes sont dans une liste spécifiée par l'utilisateur de Type ids

J'exprimer en sql comme (étant donné tous les paramètres facultatifs ont été fournis):

SELECT  
    [Session].* 
FROM   
    [OperationTypeOperation] 
LEFT OUTER JOIN 
    [Operation] ON [OperationTypeOperation].[OperationId] = [Operation].[OperationId] 
RIGHT OUTER JOIN 
    [Session] ON [Operation].[OperationId] = [Session].[OperationId] 
WHERE 
    ([Operation].[IsPublished] = 1) 
AND 
    ([Operation].[IsActive] = 1) 
AND 
    ([Session].[RegionId] = 66) 
AND 
    ([Session].[DivisionId] = 99) 
AND 
    ([Operation].[AdvertisingStartDate] < GETDATE()) 
AND 
    ([Operation].[AdvertisingEndDate] > GETDATE()) 
AND 
    ([OperationTypeOperation].[OperationTypeId] IN (1, 2, 3)) 

Et dans ma requête NHibernate comme:

public PagedResult<Session> Search(int? regionId, int? divisionId, DateTime? startDate, DateTime? endDate, IList<int> operationTypeId, int itemsPerPage, int page) 
     { 

      var criteria = _session.CreateCriteria(typeof(Session)) 
       .Add(Expression.Eq("IsActive", true)) 
       .Add(Expression.Eq("AcceptingApplications", true)) 
       .AddOrder(new Order("StartDate", false)) 
       ; 

      if (regionId.HasValue) 
       criteria.Add(Expression.Eq("Region.Id", regionId.Value)); 

      if (divisionId.HasValue) 
       criteria.Add(Expression.Eq("Division.Id", divisionId.Value)); 

      if (startDate.HasValue) 
       criteria.Add(Expression.Ge("StartDate", startDate.Value)); 

      if (endDate.HasValue) 
       criteria.Add(Expression.Le("EndDate", endDate.Value)); 

      //Add the operation types 
      if (operationTypeId.Count > 0) 
      { 
       var operationTypes = new Collection<OperationType>(); 
       foreach (int id in operationTypeId) 
       { 
        operationTypes.Add(_session.Get<OperationType>(id)); 
       } 
       //Join on the operations 
       criteria.CreateCriteria("Operation") 
       .Add(Expression.Eq("IsPublished", true)) 
       .Add(Expression.Eq("IsActive", true)) 
       .Add(Expression.Le("AdvertisingStartDate", DateTime.Now)) 
       .Add(Expression.Ge("AdvertisingEndDate", DateTime.Now)) 
       .CreateAlias("OperationTypes", "operationTypes", JoinType.InnerJoin) 
       .Add(Expression.In("OperationTypes", operationTypes)) 
       .SetResultTransformer(new DistinctRootEntityResultTransformer()) 
       ; 
      } 
      else 
      { 
       //Join on the operations 
       criteria.CreateCriteria("Operation") 
       .Add(Expression.Eq("IsPublished", true)) 
       .Add(Expression.Eq("IsActive", true)) 
       .Add(Expression.Le("AdvertisingStartDate", DateTime.Now)) 
       .Add(Expression.Ge("AdvertisingEndDate", DateTime.Now)) 
       ; 
      } 

      return criteria.ToPagedResult<Session>(itemsPerPage, page); 
     } 

Ma fonction nHibernate se plaint des types d'opération et émet une exception "Impossible d'utiliser des collections avec InExpression". De plus, je ne suis pas sûr de filtrer correctement les tables jointes. Quelqu'un peut-il peser de la bonne façon pour écrire le sql ci-dessus comme une expression nHibernate?

Répondre

5

Je suppose que OperationType est une classe d'entité (pas une énumération). Vous ne pouvez pas utiliser In avec une liste d'entités. Vous pouvez rejoindre en utilisant l'identifiant.

criteria 
    .CreateCriteria("Operation") 
    // add other expressions 
    .CreateCriteria("OperationTypes", "operationTypes", JoinType.LeftOuterJoin) 
    .Add(Expression.In("operationTypes.Id", operationTypeId)) 

Je suppose que _session.Get (id) effectue une requête de base de données. Vous devriez éviter cela de toute façon. PS: Si OperationType est mappé en tant que collection composite (avec le tag <composite-element>), vous ne pouvez malheureusement pas le joindre avec des critères. Je ne suis pas sûr s'il y a une solution de contournement. Mais il y a un patch en attente.

1

Je pense qu'il ya une meilleure façon que l'écriture:

criteria 
    .CreateCriteria("Operation") 
    // add other expressions 
    .CreateCriteria("OperationTypes", "operationTypes", JoinType.LeftOuterJoin) 
    .Add(Expression.In("operationTypes.Id", operationTypeId)) 

car joindre supplémentaire est effectuée sur "Operation"

Vous pouvez simplement écrire:

criteria.Add(Expression.In("OperationTypes", operationTypeId)) 
.CreateCriteria("Operation") 
         .Add(Expression.Eq("IsPublished", true)) 
         .Add(Expression.Eq("IsActive", true)) 
         .Add(Expression.Le("AdvertisingStartDate", DateTime.Now)) 
         .Add(Expression.Ge("AdvertisingEndDate", DateTime.Now)) 
         .SetResultTransformer(new DistinctRootEntityResultTransformer()) 
Questions connexes