2009-12-03 2 views
2

Disons que j'ai un bon de classe:NHibernate - comment obtenir un élément non référencé par un élément dans une autre table

public class Voucher 
{ 
    public Guid Id {get;set;} 
    public DateTime DateAvailable {get;set;} 
} 

et une entrée de classe

public class Entry 
{ 
    public Guid Id {get;set;} 
    public Voucher Voucher {get;set;} 
    // ... other unrelated properties 
} 

Comment puis-je créer une requête NHibernate Criteria qui trouve le premier bon disponible qui n'est pas actuellement assigné à une entrée?

SQL équivalent serait

select 
    v.Id, v.DateAvailable 
from 
    Voucher v 
     left join Entries e on e.VoucherId = v.Id 
where 
    v.DateAvailable <= getutcdate() and 
    e.Id is null 

Edit: Je suis toujours incapable de comprendre cela. La table Voucher n'a aucune référence à la table Entries, mais je dois trouver le premier coupon (par date) qui n'a pas été affecté à une entrée. Cela semble être une tâche si simple, mais tout ce que je continue à lire à propos de l'utilisation des jointures à gauche de critères NHibernate nécessite que l'objet Voucher ait une propriété qui référence l'entrée. Il existe sûrement un moyen d'inverser la requête ou d'ajouter une propriété de référence à l'objet Voucher sans modifier la base de données pour que chaque table référence l'autre.

Édition 2: Pour ce que ça vaut, je ne pense pas qu'il soit possible de faire ce que j'essayais de faire sans quelques modifications. J'ai finalement obtenu une requête pour travailler en utilisant l'entrée comme critère principal avec le voucher comme sous-critère, mais alors UniqueResult retournait null, même si les données étaient là. Je suppose que ça ne pouvait pas faire l'association.

Au cas où quelqu'un se heurterait à cela, j'ai fini par créer une clé étrangère dans chaque table qui fait référence à l'autre et en utilisant le mappage des références <> pour associer les deux. Ce n'est pas une idée, parce que je dois définir manuellement la propriété de chaque entité à l'autre pour rendre l'association bidirectionnelle, mais cela fonctionne au moins sans une tonne de changements.

Répondre

1

Traduire SQL littéralement:

var voucher = NHibernateSessionManager.Session.CreateCriteria<Voucher>("v") 
         .Add(Restrictions.Le("v.DateAvailable", DateTime.UtcNow)) 
         .CreateCriteria("Entries", "e") 
         .Add(Restrictions.IsNull("e.Id")) 
         .SetMaxResults(1) 
         .UniqueResult<Voucher>(); 

Maintenant, si je comprends bien il peut y avoir un alterantif. Si l'instruction: "..affecte le premier chèque disponible qui n'est PAS actuellement affecté à une entrée ..." est la même chose que la déclaration "trouve le premier chèque disponible qui n'a pas d'entrées ..." (puisque le chèque est l'entité qui a de nombreuses entrées ... selon vos classes) alors vous pouvez faire:

var voucher = NHibernateSessionManager.Session.CreateCriteria<Voucher>() 
       .Add(Restrictions.IsEmpty("Entries")) 
       .Add(Restrictions.Le("DateAvailable", DateTime.UtcNow)) 
       .SetMaxResults(1) 
       .UniqueResult<Voucher>(); 

... en supposant que vous avez tracé la propriété entrées dans l'entité Bon.

Mais peut-être que je me suis trompé ...

+0

Le problème est que l'objet Voucher n'a pas de référence à l'entrée à laquelle il peut être affecté. Si c'était le cas, ce serait beaucoup plus facile. Des suggestions (autres que l'ajout d'une référence/colonne)? – Chris

+0

Si vous avez l'option, il n'y a pas de mal à mettre la propriété dans l'entité Voucher. Cela ne nécessiterait pas de changement de schéma de base de données. Si vous ne pouvez pas ou ne voulez pas faire cela, il n'y a aucun moyen de l'utiliser avec ICriteria ou l'API IQuery.Je suggère d'utiliser la méthode CreateSQLQuery() qui vous permet d'envoyer une instruction SQL native. – tolism7

+0

Je vous ai donné la réponse parce que c'était le plus proche de ce que j'avais besoin de faire. – Chris

0

De mémoire, pourrait ne pas fonctionner:

session.CreateQuery<Entry>().CreateAlias("Voucher","v").Add(Restrictions.And(
    Restrictions.Lt("DateAvailable",DateTime.Now), 
    Restrictions.Eq("v.Id",null") 
).List<Voucher>(); 
1

Les éléments suivants doivent travailler à l'aide HQL:

IQuery query = session.CreateQuery(@" 
    FROM 
     Voucher v LEFT JOIN 
     Entry e 
    WHERE 
     e.ID IS NULL AND 
     v.DateAvailable <= :now 
"); 

query.SetParameter("now", DateTime.Now); 

// If you want to restrict to only the first result 
query.SetMaxResults(1); 
Voucher v = query.UniqueResult<Voucher>(); 

// Otherwise, get a list of results... 
// List<Entry> results = query.List<Entry>(); 
Questions connexes