Как заблокировать NHibernate от чтения дочерней коллекции? - PullRequest
0 голосов
/ 20 апреля 2019

У меня есть список документов в моей базе данных. Каждый документ имеет коллекцию дочерних элементов. Теперь мой сценарий таков: - читать документы (только из таблицы документов) - закрыть сеанс и позволить пользователю выполнить какую-то работу.

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

Так есть ли способ сказать nHibernate - «эй, никогда не читайте эту коллекцию!»?

Ответы [ 3 ]

1 голос
/ 20 апреля 2019

Установите Ленивую загрузку вашей коллекции на Lazy или Extra, возможно, ваша установлена ​​на NoLazy (иначе - готовность к загрузке).

Лучше установить его на Extra вместо Lazy.Поскольку это не позволит NHibernate извлекать строки для дочерней коллекции, когда вы хотите получить только .Count() или .Any() дочерней коллекции.Extra походит на более ленивую версию ленивых :)

С NoLazy / eager-loading :

var post = session.Get<Post>(1);

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

Использование Lazy, var post = session.Get<Post>(1) приведет ктолько загружая одну строку из таблицы сообщений, NHibernate не будет читать комментарии дочерней коллекции сообщения из базы данных.

Что касается Lazy vs Extra

Использование Lazy :

var commentsCount = post.Comments.Count()

Это загрузит комментарии поста из базы данных:

select * from comments where post_id = 1;

И .Count(), происходит только на стороне приложения.

Использование Extra , var commentsCount = post.Comments.Count(), NHibernate будет выдавать только запрос подсчета вместо чтения всех строк.

select count(*) from comments where post_id = 1

Вот пример конфигурации для установки механизма загрузки дочерней коллекции, есливы используете автоматическое отображение NHibernate, вы устанавливаете эти настройки на BeforeMapSet событие:

enter image description here

И когда вам нужно загружать дочернюю коллекцию, настроенную как Lazy или Extra,использовать FetchMany

0 голосов
/ 24 апреля 2019

Я обнаружил, что приводит к загрузке периодов документа из базы данных, даже если у вас нет доступа к свойству периодов документа.

namespace NHibernateFetchJoinTest2
{
    using System;

    using NHibernateFetchJoinTest2.DomainMapping;
    using NHibernateFetchJoinTest2.Domains;

    class MainClass
    {
        public static void Main(string[] args)
        {
            using (var session = Mapper.SessionFactory.OpenSession())
            {
                Console.WriteLine("SQL produced: ");

                var d = session.Get<Document>(1);

                Console.ReadLine();

                //Console.WriteLine("Document's periods: ");

                //foreach (var period in d.Periods)
                //{
                //    Console.WriteLine($"* {period.PeriodDescription}");
                //}

                Console.ReadLine();
            }
        }
    }
}

Производит это:

SQL produced: 
NHibernate:  
    SELECT
        document0_.Id as id1_0_1_,
        document0_.DocumentDescription as documentdescription2_0_1_,
        periods1_.DocumentId as documentid3_1_3_,
        periods1_.Id as id1_1_3_,
        periods1_.Id as id1_1_0_,
        periods1_.PeriodDescription as perioddescription2_1_0_ 
    FROM
        Document document0_ 
    left outer join
        Period periods1_ 
            on document0_.Id=periods1_.DocumentId 
    WHERE
        document0_.Id=@p0;
    @p0 = 1 [Type: Int32 (0:0:0)]

Ваши сопоставления похожи на следующие. Загрузка вашей дочерней коллекции Lazy установлена ​​на Lazy (в отличие от NoLazy), а стратегия Fetch установлена ​​на Join. Для остроумия:

namespace NHibernateFetchJoinTest2.DomainMapping.Mappings
{
    using NHibernate.Mapping.ByCode.Conformist;
    using NHibernateFetchJoinTest2.Domains;

    public class DocumentMapping : ClassMapping<Document>
    {
        public DocumentMapping()
        {
            Id(x => x.Id);

            Property(x => x.DocumentDescription);

            Bag(x => x.Periods, collectionMapping =>
            {
                collectionMapping.Inverse(true);
                collectionMapping.Key(k => k.Column("DocumentId"));

                collectionMapping.Lazy(NHibernate.Mapping.ByCode.CollectionLazy.Lazy);

                // Remove this. This causes Document's Periods to load, 
                // even if child collection Periods is not accessed yet.
                // This is evident in SQL log, it shows LEFT JOIN Period.
                collectionMapping.Fetch(NHibernate.Mapping.ByCode.CollectionFetchMode.Join);
            }, mapping => mapping.OneToMany());
        }
    }

    public class PeriodMapping: ClassMapping<Period>
    {
        public PeriodMapping()
        {
            Id(x => x.Id);
            Property(x => x.PeriodDescription);
        }
    }
}

Если это будет удалено ...

collectionMapping.Fetch(NHibernate.Mapping.ByCode.CollectionFetchMode.Join);

... дочерняя коллекция Periods не получена преждевременно родителем (Документ):

SQL produced: 
NHibernate: 
    SELECT
        document0_.Id as id1_0_0_,
        document0_.DocumentDescription as documentdescription2_0_0_ 
    FROM
        Document document0_ 
    WHERE
        document0_.Id=@p0;
    @p0 = 1 [Type: Int32 (0:0:0)]

Используемые шаги воспроизведения: https://github.com/MichaelBuen/NHibernateFetchJoinTest2

0 голосов
/ 21 апреля 2019

В качестве временного решения я создал простой хак:

public class Document
{
    IList<Periods> periods;
    public virtual IList<Period> Periods
    {
        get { return periods; }
        set { periods = value; }
    }

    public virtual void ResetPeriods()
    {
        periods = new List<Period>();
    }
}

И вот как я получаю документы:

db.BeginTransaction();
IList<Document> list = db.Get<Document>();
db.CommitTransaction();

List<Document> result = new List<Document>();
foreach (var item in list)
{
    item.ResetPeriods(); //todo: HACK! Preventing from lazy load of periods
    result.Add(item);
}


return result;

Конечно, эта коллекция отображается как ленивая.Дочерняя коллекция (Periods) должна быть определена как обратная переменная, поскольку она не позволяет NHibernate Proxy использовать свойство getter.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...