Загрузка многоуровневых коллекций без дубликатов в NHibernate - PullRequest
3 голосов
/ 30 сентября 2011

Мой вопрос очень похож на этот (на который действительно не было ответа): Nhibernate: отличные результаты в коллекции второго уровня

У меня есть эта объектная модель:

   class EntityA
   {
        ...
        IList<EntityB> BList { get; protected set; }
        ...
   }

   class EntityB
   {
       ... does NOT reference its parent EntityA...
       IList<EntityC> CList { get; protected set; }
   }

Это отношения один-ко-многим.EntityB и C не имеют ссылку на объект для своего родительского объекта.

Я бы хотел полностью загрузить коллекции, выполнив что-то вроде следующих трех запросов SQL, чтобы избежать декартового объединения:

 SELECT id, ... FROM EntityA;
 SELECT id, idA, ... FROM EntityB;
 SELECT id, idB, ... FROM EntityC;

При этом DAL обладает всей информацией для правильного заполнения объектов.Но поскольку EntityB не знает, кто его родитель, он имеет для nHibernate, который позаботится о правильном заполнении коллекций.

Можно ли это сделать ??


Я мог бы сделать этот обходной путь с Декартовым продуктом, но это требует модификации моей модели, чтобы обеспечить установку сборщика, и это квалифицируется как исправление для технической проблемы с DAL в моем уме.

     ICriteria criteria = session.CreateCriteria<EntityA>()
                         .SetFetchMode("BList", FetchMode.Join)
                         .SetFetchMode("BList.CList", FetchMode.Join)
                         .SetResultTransformer(new DistinctRootEntityResultTransformer());

     IList<EntityA> listA = criteria.List<EntityA>();

     foreach (EntityA objA in listA) {
        objA.BList = objA.BList.Distinct().ToList();
        foreach (EntityB objB in objB.BList) {
           objB.CList = objB.CList.Distinct().ToList();
        }
     }

1 Ответ

5 голосов
/ 01 октября 2011

Вы пробовали этот синтаксис:

var entities = session.QueryOver<EntityA>().Where(...).List();
var entityIds = entities.Select(e => e.Id).ToArray();
session.QueryOver<EntityA>()
    .WhereRestrictionOn(a => a.Id)
    .IsIn(entityIds)
    .Fetch(e => e.BList).Eager
    .List();

var bEntityIds = entities
    .SelectMany(e => e.BList)
    .Select(b => b.Id)
    .ToArray();

session.QueryOver<EntityB>()
    .WhereRestrictionOn(b => b.Id)
    .IsIn(bEntityIds).Fetch(e => e.CList).Eager
    .List();

Это должно запустить три выбора, которые вы упомянули. Это может показаться немного запутанным, но он использует кеш первого уровня сеанса, который гарантирует, что все сущности в первой коллекции обновляются загруженными коллекциями по мере их выполнения.

Кроме того, вы не платите штраф за сложную логику запросов, которую вы можете использовать для второго и третьего запросов. БД должна использовать первичный индекс для второго запроса (может быть, даже кластеризованный в зависимости от ваших настроек) и внешний ключ для объединения для чрезвычайно дешевых запросов.

...