2011-08-14 13 views
5

Mam prosty model, składający się z dokumentu, który odwołuje się do jednego lub więcej artykułów za pomocą obiektu odniesienia (dzieje się tak dlatego, że w domenie nie jesteśmy jej właścicielami artykuły, abyśmy mogli je tylko odwoływać).Jak wybrać i spożyć zbiór obiektów wartości w kwerendzie NHibernate QueryOver

Próbuję napisać zapytanie zawierające listę dokumentów, drukowanie identyfikatora i ciąg złożony z rozdzielanej przecinkami listy numerów artykułów. Na przykład:

ID ARTICLES 
------------------ 
1 ACC, PE2, 
2 ER0, AQ3, FEE 
3 PE2 

Mój problem polega na wybraniu z listy oddzielonych przecinkami.

Oto klas domeny:

// The Entity class has an Id property. 
public class Document : Entity 
{ 
    public virtual IEnumerable<ArticleReference> ArticleReferences { get; set; } 
    public virtual DateTime ReceiveDate { get; set; } 
} 

// The ValueObject does not have an Id property ofcourse. 
public class ArticleReference : ValueObject 
{ 
    public virtual string ArticleNumber { get; set; } 
    public virtual string ArticleName { get; set; } 
} 

Odniesienie artykuł jest obiektem wartość więc nie ma identyfikatora własnych.

Jest to model widoku, który reprezentuje pozycję na liście wyników:

public class DocumentListItemModel 
{ 
    public int Id { get; set; } 
    public string ArticleNumbers { get; set; } 
    public string ReceiveDate { get; set; } 
} 

A oto klasa zapytania mam wymyślić do tej pory:

public class DocumentQuery 
{ 
    public IList<DocumentListItemModel> ExecuteQuery() 
    { 
     IntermediateModel model = null; 
     ArticleReference articleReferenceAlias = null; 

     return Session 
      .QueryOver<Document>() 
      .JoinAlias(n => n.ArticleReferences,() => articleReferenceAlias); 
      .SelectSubQuery(
       QueryOver.Of<ArticleReference>(() => articleReferenceAlias) 
        // There is no way of matching references to documents from a domain 
        // point of view since the references are value objects and 
        // therefore don't have an ID. 
        .Where(n => ...) 
        .Select(q => articleReferenceAlias.Number)) 
       .WithAlias(() => model.ArticleNumbers) 
      .TransformUsing(Transformers.AliasToBean<IntermediateModel>()); 
      .Future<IntermediateModel>() 
      .ToList() 
      .Select(n => 
       new DocumentListItemModel() 
       { 
        Id = n.Id, 
        ArticleNumbers = string.Join(", ", n.ArticleNumbers.OrderBy(p => p)), 
        ReceiveDate = n.ReceiveDate.ToString("d", CultureInfo.CurrentCulture) 
       }) 
      .ToList(); 
    } 

    private class IntermediateModel 
    { 
     public int Id { get; set; } 
     public IEnumerable<string> ArticleNumbers { get; set; } 
     public DateTime ReceiveDate { get; set; } 
    } 
} 

Jak widać, Nie mogę wyrazić instrukcji .Where, ponieważ nie ma możliwości dopasowania odniesień do dokumentów z punktu widzenia domeny. Odwołania są obiektami wartości i dlatego nie mają identyfikatora.

Pytanie brzmi: jak to naprawić zapytania prawidłowo wybrać listę artykułów, dzięki czemu można go używać w moim string.Join oświadczeniem aby oddzielone przecinkami ciąg?

+0

Czy próbowałeś dodać publiczne int? DocumentId {get; set;} to ArticleReference? ps nie są właściwościami, które mają być wirtualne w nhibernate –

+0

Masz rację co do wirtualnych, zaktualizowałem moje pytanie. Zapomniałem o nich pisząc kod pojęciowy (prawdziwy kod jest zbyt rozpraszający ze względu na jego rozmiar). Odnośnie dodawania DocumentId do ArticleReference: byłoby to sprzeczne z całą ideą obiektów wartościowych, skutecznie przekształcając ją w podmiot. –

+0

prawda - ale z powodu takich problemów mam tendencję do posiadania osobnego modelu domeny do mojego modelu aplikacji, więc dodam funnie, takie jak dodanie właściwości parentid do elementu podrzędnego. –

Odpowiedz

0

Udało mi się rozwiązać problem. To, co skończyło się z:

public IList<DocumentListItemModel> ExecuteQuery() 
{ 
    ArticleReference articleReferenceAlias = null; 

    return Session 
     .QueryOver<Document>() 
     .JoinAlias(n => n.ArticleReferences,() => articleReferenceAlias, 
      JoinType.LeftOuterJoin) 
     .SelectList(list => list 
      .Select(n => n.Id) 
      .Select(n => articleReferenceAlias.Number)) 
     .List<object[]>() 
     .Select(x => new 
     { 
      Id = (int)x[0], 
      ArticleNumber = (string)x[1] 
     }) 
     .GroupBy(n => n.Id).Select(n => 
     { 
      return new DocumentListItemModel 
      { 
       Id = n.First().Id, 
       ArticleNumbers = string.Join(", ", n.Select(p => p.ArticleNumber)) 
      }; 
     }).ToList(); 
    } 
} 

nie mogłem użyć transformatora alias-to-już fasoli, ponieważ nie może obsługiwać właściwości kolekcji. Dlatego rozwiązanie ma GroupBy, konsoliduje wiersze, agregując numery artykułów w ciąg.

1

Myślę, że przyjmujecie definicję przedmiotu wartości zbyt dosłownie. Przypisanie zastępczego identyfikatora (kolumny tożsamości, Guid itp.) Do obiektu wartości nie czyni go mniejszym od obiektu wartości. Jest to value object, ponieważ jego równość opiera się na wartościach, a nie na tożsamości. Nie wymaga to, aby obiekt wartości nie posiadał tożsamości, a w praktyce prawie zawsze musi.

Twoja aplikacja oczywiście musi być w stanie połączyć dokument z zestawem ArticleReferences i najlepszym sposobem osiągnięcia tego jest dodanie ID do ArticleReference.

+1

Jeśli chodzi o nadawanie obiektowi wartości tożsamości, mogę jedynie myśleć o ważnych przyczynach, które dotyczą kompensowania ograniczeń technicznych w innym miejscu, na przykład w warstwie trwałości. Z punktu widzenia domeny tożsamość obiektu wartości jest zbędna (chyba że sama domena jest wadliwa), ponieważ obiekt wartości JEST tożsamością. Rozumiem, skąd przybywasz, ale odkąd udało mi się wypracować rozwiązanie (które zamieszczę tutaj wkrótce) w związku z tym problemem, znowu nie ma potrzeby dodawania tożsamości, więc będę trzymać się mojego punktu widoku do odwołania :) –

Powiązane problemy