Myślisz, że zarabiasz tyle, na ile zasługujesz? Zapraszamy do wzięcia udziału w anonimowej ankiecie.
3

1

Kolejne pytanie o nhib.

Klasy i mapowanie jak w pytaniu, w którym już mi pomogliście:

 public class Task
    {
     public virtual int Id {get;set;}
     public virtual Person Author { get; set; }
     public virtual string Title { get; set; }
     public virtual IList<Domain.TaskAssignement> Assignements { get; set; }

    }

    public class Person
    {
    public virtual int Id {get;set;}
    public virtual string Name {get;set;}
    ...
    }

    public class TaskAssignement
    {
    public virtual int Id {get;set;}
    public virtual Person Person { get;set; }
    public virtual Task Task { get;set; }
    public virtual DateTime JoinDate {get;set;}
    public virtual bool IsDeleted {get;set;}
    }

Czyli jedno zadanie ma kilka osób przypisanych do jego wykonania, jedna osoba ma wiele zadań. Sytuacja dość standardowa.

Teraz pytanie. Potrzebuję w zależności od kryteriów wyszukiwania wprowadzanych przez użytkownika wybierać z bazy te zadania, które je spełniają. Doszedłem do miejsca, w którym jestem trochę zagubiony.

Assignements w pojedynczym Task posiadają dowiązanie do osoby. Użytkownik aplikacji chce wyszukać wszystkie zadania przypisane do użytkownika o ID = X. Jednocześnie załadowanie pełnych list Assignements w każdym z zadań nie jest niezbędne - grid w którym pokazywane są zadania nie korzysta z nich.

W pierwszym podejściu ściągałem listę zadań i używając LINQ filtrowałem ją po stronie C#, to jednak wywoływało lazy-loading i N+1 zapytań dla każdego wiersza zadania. Nie chcę wyłączać lazy-loadingu dla "Bag" Assignements - przyda się w innym miejscu.

Jak skonstruować zapytanie w NHibernate, aby:

  • zminimalizować liczbę niepotrzebnych zapytań (200 zadań * 3 przypisania - robi się sporo)
  • przefiltrować zapytania po stronie DB w zależności od assignements jednak nie ściągać ich
  • w ostateczności - ściągnąć zadania (wraz z ich assignements) jednak tylko te, które spełniają zadane kryteria

Mam do wykorzystania: NHibernate 2, C# 3.5, Fluent, NHibernate.Linq

Tutaj znalazłem pośrednie rozwiązanie:

string sql = "from Order o" +
                     " inner join fetch o.OrderLines" +
                     " inner join fetch o.Customer" +
                     " where o.Id=:id";
        fromDb = session.CreateQuery(sql)
                        .SetGuid("id", _order.Id)
                        .UniqueResult<Order>();

Czy da się je wygenerować dla listy używając NHibernate.Linq ?

Jak Wy radzicie sobie z podobnymi zapytaniami / problemami? Generalnie chciałbym załadować listę zadań w następującym algorytmie:

  1. Użytkownik wprowadza warunki wyszukiwania
  2. Tworzę ISession, z ITaskRepository wyciągam tylko zadania spełniające warunki
  3. Zamykam ISession przypisując wynik do listy lokalnej
  4. Listą wypełniam grida, z pewnością, że nie zaskoczy mnie LazyloadingException związany z zamkniętą sesją

Czy taki scenariusz jest dobry? Jak Wy realizujecie takie zadania?

flag
Rozumiem, że to nie jest webowa aplikacja? – dario-g Feb 17 at 13:21
Nie - to nie jest webowa aplikacja. Póki co zaczynam z NHIbernate w klasycznych Windows Forms. – andrzejp Feb 17 at 15:15
Tu niestety nie mam doświadczenia :) Poza tym ja wolę pisać query używając ICriteria lub NHIbernate.LINQ. W obu przypadkach masz możliwość ustawienia SetFetchMode("właściwość", NHibernate.FetchMode.Join/Select); Dzięki temu właściwość może być zamapowana na lazy, ale w danym zapytaniu zostanie odrazu zaciągnięta. – dario-g Feb 18 at 11:28

1 Answer

1

Ok, po niezłych "kilku" godzinach czytania udało mi się znaleźć satysfakcjonujące rozwiązanie.

Udało się jednocześnie uniknąć ładowania zależności i pisania SQL w kodzie C#. Rozwiązanie niemal idealne ;)

Przy użyciu ICriteria daje się pisać podzapytania - o czym wcześniej nie miałem pojęcia.

Rozwiązanie mojego problemu:

  • Tworzymy ICrieria dla podzapytania
  • Tworzyny ICriteria dla głównego obiektu
  • Dołączamy ICriteria podzapytania do ICritera głównego obiektu z odpowiednią zależnością

    //nowe "odłączone" warunki dla TaskAssignement - czyli Bag w Task

    DetachedCriteria assignementCriteria = DetachedCriteria.For;

    //informujemy, że chcemy tylko Id

    assignementCriteria.SetProjection(Projections.Property("Id"));

    //i dodatkowo tylko te rekordy, w których przypisany użytkownik ma Id == szukanemu

    assignementCriteria.CreateCriteria("Person").Add(Restrictions.Eq("Id", conditions.AssignedUserId));

    //tworzymy kryteria dla głównego obiektu

    session.CreateCriteria()

    //dodajemy informację o tym, że Id zadania ma zawierać się w identyfikatorach wyszukanych podzapytaniem

    .Add(Subqueries.PropertyIn("Id", assignementCriteria)) .List();

Gotowe, wszystko działa i niepotrzebnie nie pobieramy obiektów zależnych a jedynie "odpytujemy" o nie bazę w warunkach wyszukiwania.

link|flag

Your Answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.