2014-04-07 4 views
5

Ich habe ein Problem mit der Auswahl einer Zeichenfolge Sammlung und habe es reproduziert mit dem folgenden kleinen Beispiel.NHibernate Abfrage auf eine Zeichenfolge Sammlung mit Linq führt entweder Fehler oder leere Sammlung

die folgende SQL Gegeben:

CREATE TABLE [Post] (
    [Id] INT  IDENTITY NOT NULL, 
    [Name] NVARCHAR(20) NOT NULL, 
    CONSTRAINT [PK_Post] PRIMARY KEY CLUSTERED ([Id]) 
) 
CREATE TABLE [Category] (
    [Id] INT  IDENTITY NOT NULL, 
    [PostId] INT NOT NULL, 
    [Name] NVARCHAR(20) NOT NULL, 
    CONSTRAINT [FK_Category_Post] FOREIGN KEY ([PostId]) REFERENCES [Post]([Id]) 
) 
INSERT INTO [Post] ([Name]) VALUES ('Post 1') 
INSERT INTO [Category] ([PostId], [Name]) VALUES (1, 'Alpha') 

Und der Code (I verwendet LINQPad):

void Main() 
{ 
    using (var sessionFactory = Fluently.Configure() 
     .Database(MsSqlConfiguration.MsSql2008.Dialect<MsSql2012Dialect>().ConnectionString(@"Data Source=(localdb)\Projects;Initial Catalog=NhTest;")) 
     .Mappings(x => { 
      x.FluentMappings.Add(typeof(PostMap)); 
     }) 
     .BuildSessionFactory()) 
    using (var session = sessionFactory.OpenSession()) 
    { 
     var post = session.Get<Post>(1); 
     Debug.Assert(post.Categories.First() == "Alpha"); 

     try { 
      var second = session.Query<Post>() 
       .Where(x => x.Id == 1) 
       .Select(x => new { 
        x.Categories, 
        x.Name, 
       }) 
       .Single(); 
     } 
     catch (Exception ex) { 
      Debug.Fail(ex.ToString()); 
     } 

     var third = session.Query<Post>() 
      .Where(x => x.Id == 1) 
      .Select(x => new { 
       x.Categories, 
       x.Name, 
      }) 
      .ToList().First(); 

     Debug.Assert(third.Categories.Count() == 1, "Category count was " + third.Categories.Count()); 
    } 
} 

// Define other methods and classes here 
class Post 
{ 
    public virtual int Id { get; protected set; } 
    public virtual string Name { get; protected set; } 
    public virtual IList<string> Categories { get; protected set; } 
} 

class PostMap : ClassMap<Post> 
{ 
    public PostMap() 
    { 
     Id(x => x.Id); 
     Map(x => x.Name); 

     HasMany(x => x.Categories) 
      .Table("Category") 
      .Element("Name") 
      .KeyColumn("PostId"); 
    } 
} 

Die ersten assert Pässe und dies, in meinem Kopf, bestätigt meine Zuordnung der Kategorien auf die Post.

Die Abfrage im try-Block löst eine Ausnahme

'System.Linq.EnumerableQuery`1[System.Collections.Generic.IList`1[System.String]]' cannot be converted to type 'System.Linq.IQueryable`1[System.Object[]] 

So habe ich es in den 3. Versuch geändert Sie den Code zu sehen, rufen .ToList().First(). Diese Abfrage löst keine Ausnahme aus, aber die Kategorienliste wird leer zurückgegeben.

Mache ich etwas falsch? Gibt es hier eine bessere Kartierungstechnik? Oder gibt es Workarounds, um meine Anfrage zu bearbeiten?

Antwort

5

Dies war ein Fehler in NHibernate. Es würde versuchen, die Klassenzuordnungsdaten für den Parameter Enumerable Type abzurufen. Bei der Projektion einer Enumerable einer Map-Klasse funktioniert das perfekt. In diesem Fall ist der Parameter Enumerable type jedoch string. String hat keine Klassenzuordnung und daher wurde in der Abfrage eine Nullkonstante verwendet, die später Probleme verursachte.

Um dies weiter zu veranschaulichen, die SQL-Abfrage für dieses Beispiel wäre:

SELECT c.Name, p.Id, p.Name FROM Post p LEFT JOIN Category c ON p.Id = c.PostId WHERE p.Id = 1 

Dies würde Daten zurückgeben wie so:

c.Name | p.Id | p.Name 
-------------------------- 
Cat 1 | 1  | Post A 
Cat 2 | 1  | Post A 

NHibernate würde dann im Speicher, eine Gruppe durchführen, indem Operation, einschließlich Null-Prüfungen, um eine Liste von Kategorien für jeden eindeutigen Wert von p.Id zu erstellen. Die Gruppierung nach Operationen wird unter Verwendung von Spaltenindizes durchgeführt.

Das sollte was passieren. Der Fehler wurde bewirkt, dass die Ergebnisse, bevor die Gruppe durch den Betrieb umgewandelt werden, in:

NULL | Cat 1 | 1  | Post A 
NULL | Cat 2 | 1  | Post A 

NHibernate wurde dann Ergebnisse Filtern wo Spalte 0 war null, was bedeutet, keiner von ihnen überleben.

Der Antrag Pull das Update enthält, ist hier: https://github.com/nhibernate/nhibernate-core/pull/262

1

Sie können CategoryMap, Category Entität erstellen und ändern:

public virtual IList<Category> Categories { get; protected set; } 

Ich bin Vermutung eine Linq zu nhibernate Abfrage Einschränkung ist.

+0

ich eine neue Kategorie Klasse erstellen als mit einer Liste von Strings passt meine Domain besser wirklich vermeiden wollte. Ich habe das auch mit QueryOver versucht und bekomme den gleichen Fehler. – Matt

1

Try

var second = session.Query<Post>() 
    .Where(x => x.Id == 1) 
    .Select(x => new { 
     x.Categories, 
     x.Name, 
    }) 
    .Single(); 

Um ToList().Single() statt Single() ändern.

Ich habe dieses Problem zuvor gesehen, das Problem besteht darin, dass die Abfrage mehrere Zeilen zurückgibt, die dann zu einem einzigen anonymen Typ gezwungen werden. Die Abfrage schlägt fehl, da die Zwischenergebnisse mehrere Zeilen anstelle einer einzelnen Zeile enthalten.

+0

Das ist, was ich in der dritten Abfrage in meinem Beispielcode getan habe, aber ich bekomme falsche Ergebnisse. – Matt

+0

Tut mir leid, dass ich darauf gesprungen bin, ohne das Ganze zu lesen. Ich habe eine fast identische Abfrage erstellt und das erwartete Ergebnis zurückgegeben. Haben Sie das SQL, das an die Datenbank gesendet wurde, profiliert? Ich sehe, dass Sie eine lokale Datenbank verwenden, meine Vermutung ist, dass Sie nicht die richtige Datenbank treffen. –

+0

Ich habe die Abfrage ja profiliert, und es wählt die richtigen Werte aus. Ich gehe im Moment durch den NH-Quellcode, es sieht so aus, als würde es eine falsche clientseitige Transformation erzeugen. – Matt