2011-01-04 8 views
0

Ich möchte eine Eins-zu-viele-Beziehung auf einer Entität abfragen. Um zu helfen, mein Problem weiter zu erläutern, stellen Sie sich meine Anwendung die folgenden Einheiten hat:Nhibernate - Query Eins-zu-viele-Beziehung mit Linq

public class User { 
    public virtual int UserID { get; set; } 

    public virtual Membership CurrentMembership { 
     get { return Membership.Single(m => m.IsValid); } 
    } 

    public virtual IList<Membership> Membership { get; private set; } 

    public User() { 
     Membership = new List<Membership>(); 
    } 
} 

public class Membership { 
    public virtual int MembershipID { get; set; } 
    public virtual User User { get; set; } 
    public virtual DateTime StartDate { get; set; } 
    public virtual DateTime? EndDate { get; set; } 
    public virtual int DaysLeft { get; set; } 
    public virtual bool IsValid { get; set; } 

    public Membership() { 
    } 
} 

Mit dem folgende Fluent Mapping (die Abbildung ist eine Art irrelevant, ich habe das hier nur zur Orientierung) setzen:

public class UserMap : ClassMap<User> { 
    public UserMap() { 
     Table("Users"); 
     Id(x => x.UserID); 
     HasMany(x => x.Membership) 
      .KeyColumn("UserID") 
      .Inverse() 
      .Cascade.All(); 
    } 
} 

public class MembershipMap : ClassMap<Membership> { 
    public MembershipMap() { 
     Table("Membership"); 
     Id(x => x.MembershipID); 
     References(x => x.User); 
     Map(x => x.StartDate); 
     Map(x => x.DaysLeft) 
      .Formula("CASE WHEN EndDate IS NOT NULL AND dbo.DayDiff(GETUTCDATE(), EndDate) > 0 THEN dbo.DayDiff(GETUTCDATE(), EndDate) ELSE 0 END"); 
      // DayDiff is a udf which gets the number of days between two dates 
     Map(x => x.IsValid) 
      .Formula("CASE WHEN dbo.GetValidMembershipID(UserID) = MembershipID THEN 1 ELSE 0 END"); 
      // GetValidMembershipID is a udf which works out the valid membership id for this user 
    } 
} 

Wie Sie sehen können, kann ein Benutzer viele Mitgliedschaften haben. Die CurrentMembership (Eigenschaft für den Benutzer) gibt die Mitgliedschaft für den Benutzer zurück, bei der die IsValid-Eigenschaft wahr ist (dies gilt nur für eine einzelne Mitgliedschaft für den Benutzer).

Jetzt möchte ich in der Lage sein, alle Benutzer zu greifen, deren aktuelle Mitgliedschaft weniger als 20 Tage hat.

Mein erster Versuch war zu sagen:

session.Linq<User>().Where(u => u.CurrentMembership.DaysLeft < 20).ToList(); 

Aber das warf den Fehler:

"nicht Eigentum lösen könnte: CurrentMembership von: User"

Dies war irgendwie da ich erwartet habe keine Formelzuordnung für diese Eigenschaft verwendet (wie ich es für die DaysLeft- und IsValid-Eigenschaften für die Mitgliedschaft getan habe). Ich kann nicht sehen, wie Sie Formel Mapping anders als zum Zuordnen von Strings, Ints und Bools verwenden können. Weiter habe ich versucht zu sagen:

session.Linq<User>().Where(u => u.Membership.Single(m => m.IsValid).DaysLeft < 20).ToList(); 

Aber das warf den Fehler: „Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt“

Ich weiß, ich könnte diese Mitgliedschaft direkt abfragen, aber ich habe dies als ein Beispiel für etwas, was ich in vielen Orten. Könnte jemand eine alternative Möglichkeit vorschlagen, die CurrentMembership-Eigenschaft zuzuordnen, die es mir ermöglicht, sie mit Linq abzufragen. Bitte beachten Sie, dass ich eine optimale Leistung benötige. Die Konvertierung in eine Liste und die Arbeit im Speicher reichen nicht aus.

Ich würde die Hilfe zu schätzen wissen. Danke

Antwort

0

Sie verwenden den alten LINQ-Anbieter in NHibernateContrib für NHibernate 2.1. Dieser Anbieter wird nicht mehr unterstützt und ist auch nicht aktiv daran beteiligt.

Der in die NHibernate 3.0-Syntax integrierte LINQ-Provider ist session.Query<TEntity>() anstelle von session.Linq<TEntity>().

Nun ist NHibernate 3.0 die endgültige Version. Wenn Sie in der Vergangenheit Probleme damit hatten, wurden sie wahrscheinlich bereits gelöst. Ich habe beide für schwere Abfragen verwendet und die neue erfüllt viele Szenarien.
Vielleicht ist das Beste, um die neueste Stammquelle zu bekommen und sie zu benutzen.

Ich empfehle Ihnen dringend, Ihre Sachen mit NHibenrate 3.0 zu ersetzen. Es ist "fast" zu 100% abwärtskompatibel mit NHibernate 2.1, und alles, was Sie ändern müssen, ist session.Linq<TEntity>() bis session.Query<TEntity>().

Wenn es immer noch nicht funktioniert, können Sie versuchen u.Membership.Any(...) oder u.Membership.First(...) statt u.Membership.Single(...) im where Zustand.

Wenn dies immer noch nicht funktioniert, versuchen Sie, die Abfrage umzukehren. Fragen Sie nach Membership und wählen Sie membership.User.

CurrentMembership wird nicht funktionieren, da es nicht zugeordnet ist.

+1

Der neue Linq-Anbieter ist Quatsch. Es gibt viele Probleme, die größte ist, dass ich Fetch vor einem Skip/Take nicht aufrufen kann. Bis diese behoben sind, kann ich nicht upgraden. – nfplee

+1

Haben Sie die QueryOver API ausprobiert? Es ist meine Standard-API für NH 3.0. Sehr ähnlich wie LINQ, aber mit den meisten Merkmalen der Kriterien-APIs (es ist ein Wrapper dafür). Die alte LINQ ist leider eine Sackgasse. Irgendwelche Begrenzungen oder so werden einfach nicht behoben. – Meligy

+0

QueryOver sieht vielversprechend aus, aber ich mag die Flexibilität, die ich mit Linq bekomme. Wenn ich in Zukunft jemals meine Anwendung auf das Entity-Framework aufrüsten möchte, sollte das trivial sein. Ich habe beschlossen, bei dem alten Anbieter zu bleiben, aber ich bin gerade dabei, ein paar Komponententests zu schreiben, so dass es ziemlich schmerzlos sein sollte, wenn ich ein Upgrade auf NHibernate 3 oder das Entity Framework plane. – nfplee