Linq to NHibernate - загружать внуков, но не детей - PullRequest
3 голосов
/ 19 января 2012

Я использую NHibernate 3.2 с FluentNHibernate и Linq для NHibernate.Я хочу использовать Linq для NHibernate, чтобы загружать всех внуков коллекции без необходимости загружать детей.Например, скажем, что у меня есть следующие классы:

public class Parent
{
    public virtual int Id { get; set; }
    public virtual IList<Child> Children { get; set; }
}

public class ParentMap : ClassMap<Parent>
{
    Id(x => x.Id);
    HasManyToMany(x => x.Children).ExtraLazyLoad();
}

public class Child
{
    public virtual int Id { get; set; }
    public virtual IList<Parent> Parents { get; set; }
    public virtual IList<Grandchild> Grandchildren { get; set; }
    public virtual ProhibitivelyLargeType ProhibitivelyLargeField { get; set; }
    public virtual ProhibitivelyLargeType RarelyUsedLargeField { get; set; }
}

public class ChildMap : ClassMap<Child>
{
    Id(x => x.Id);
    HasManyToMany(x => x.Parents).ExtraLazyLoad();
    HasManyToMany(x => x.Grandchildren).ExtraLazyLoad();
    Map(x => x.ProhibitivelyLargeField);
    Map(x => x.RarelyUsedField).LazyLoad();
}

public class Grandchild
{
    public virtual int Id { get; set; }
    public virtual IList<Child> Children { get; set; }
    public virtual int Age { get; set; }
}

public class GrandchildMap : ClassMap<Grandchild>
{
    Id(x => x.Id);
    HasManyToMany(x => x.Children).ExtraLazyLoad();
    Map(x => x.Age);
}

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

Dictionary<Parent, int> grandchildAges = session.Query<Parent>()
    .FetchMany(p => p.Children)
    .ThenFetchMany(c => c.Grandchildren)
    .AsEnumerable()
    .ToDictionary(
        p => p,
        p => p.Children.SelectMany(c => c.Grandchildren).Sum(g => g.Age)
    );

Этот метод дает правильные результаты.Однако это требует загрузки всех дочерних объектов.Child включает в себя поле типа ProhibitivelyLargeType, которое загружается не лениво, поэтому я бы предпочел не загружать ничего о Child, кроме его идентификатора.Однако, если я не использую FetchMany / ThenFetchMany, у меня возникает проблема N + 1, и для каждого ребенка и внука выполняется поездка в базу данных, что также неприемлемо.

В качестве альтернативы я мог бы сделатьЗапредельно большие поля LazyLoad.Однако большинство приложений, использующих класс Child, должны использовать ProhibitivelyLargeField, но они не хотят загружать RarelyUsedLargeField, который уже является LazyLoad.Насколько я понимаю, загрузка одного свойства LazyLoad приводит к загрузке всех из них, поэтому это решение приведет к сбою в обычном случае использования.

Есть ли способ получить именно ту информацию, которую я ищу, используяLinq для NHibernate, или мне нужно использовать API Criteria Query?

Спасибо!

отредактировано, чтобы привести пример того, почему создание ProhibitivelyLargeField LazyLoad может быть нежелательным

Ответы [ 2 ]

0 голосов
/ 20 января 2012

Я не использовал nhibernate, но я использовал linq для сущностей, и из того, что я вижу, вы делаете тонны запросов к базе данных.Вместо этого вы должны выполнить однострочный запрос, который возвращает только те данные, которые вы хотите:

from parent in session.Parents
let children = parent.Children
select new {parent = parent, children.SelectMany(c => c.Grandchildren).Sum(gc => gc.Age)}

Извинения, если я что-то не так сделал.Я давно не делал C #, и я нахожусь на моем телефоне.

Если этот подход не работает, кто-то просто скажет мне, и я удалю его.

0 голосов
/ 20 января 2012

следующее QueryOver.это только показать идею загрузки результатов в два небольших шага.Может быть, вы можете перевести его на LINQ

// inititialize the dictionary
Grandchild grandchild = null;
Dictionary<Parent, int> dict = session.QueryOver<Parent>()
    .JoinQueryOver(p => p.Childs)
    .JoinAlias(c => c.GrandChilds, () => grandchild)
    .Select(Projections.Group<Parent>(p => p.Id), Projections.Sum(() => grandchild.Age))
    .AsEnumerable()
    .Cast<object[]>()
    .ToDictionary(
        array => session.Load<Parent>(array[0]),
        array => (int)array[1]
    );

// initialize all Parent proxies
session.QueryOver<Patient>()
    .WhereProperty(p => p.Id).In(dict.Keys.Select(p => p.Id))
    .ToList();
...