NHibernate: предотвращение обновления для запуска неинициализированных коллекций - PullRequest
2 голосов
/ 22 марта 2011

У меня есть сущность с коллекцией, которая содержит другие сущности, имеющие больше ссылок на сущности. Когда сущность загружается и позже обновляется, запускается отложенная коллекция, и из базы данных извлекается много материала, который мне не нужен. Итак, как я могу предотвратить запуск неинициализированных обновлений при обновлении? Я написал небольшой тест, который показывает это.

class SimpleClass
{
    public virtual int Id { get; set; }

    public virtual IList<ChildClass> Childs { get; set; }
}
class ChildClass
{
    public virtual int Id { get; set; }
}

class SimpleClassMap : ClassMap<SimpleClass>
{
    public SimpleClassMap()
    {
        Id(sc => sc.Id).GeneratedBy.Assigned();

        HasMany(sc => sc.Childs)
            .Cascade.All()
            .LazyLoad();
    }
}
class ChildClassMap : ClassMap<ChildClass>
{
    public ChildClassMap()
    {
        Id(cc => cc.Id).GeneratedBy.Assigned();
    }
}

и следующий тест

    [Fact]
    public void Test()
    {
        m_session.Save(new SimpleClass
        {
            Id = 1,
            Childs = new List<ChildClass>
            {
                new ChildClass { Id = 1 },
                new ChildClass { Id = 2 },
                new ChildClass { Id = 3 },
            }
        });
        m_session.Flush();
        m_session.Clear();
        log.Debug("Start Test");

        var simple = m_session.Get<SimpleClass>(1);

        var persistentcollection = simple.Childs as IPersistentCollection;
        Assert.False(persistentcollection.WasInitialized, "Before Refresh, collection should not be initialized but was");

        log.Debug("Refresh");
        m_session.Refresh(simple);

        Assert.False(persistentcollection.WasInitialized, "After Refresh, collection should not be initialized but was");
    }

Выход: После обновления коллекция не должна быть инициализирована, но была

Просто чтобы уточнить:

Может быть, я могу жить без удаления в cascade.all, но определенно не без cascade.refresh. Я хочу обновить каскад для коллекции, если она инициализирована, иначе нет, потому что при использовании она будет загружена свежими данными.

Я немного поиграл с NHibernate-кодом для события Refresh. Если я удалю профиль извлечения:

DefaultRefreshEventListener {
public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready)
{
    [...]
    string previousFetchProfile = source.FetchProfile;
    //source.FetchProfile = "refresh";
    object result = persister.Load(id, obj, @event.LockMode, source);
    //source.FetchProfile = previousFetchProfile;
    [...]
}

я получаю то, что ожидаю в журналах (удаленные псевдонимы в SQL):

без инициализации коллекции

Session.Get
DEBUG - SELECT Id  FROM "SimpleClass" WHERE Id=:p0;:p0 = 1
Refresh
DEBUG - SELECT Id FROM "SimpleClass" WHERE Id=:p0;:p0 = 1

с инициализирующей коллекцией (simple.Childs.Count ();)

Session.Get
DEBUG - SELECT Id  FROM "SimpleClass" WHERE Id=:p0;:p0 = 1
simple.Childs.Count();
DEBUG - SELECT SimpleClass_id, Id, Id  FROM "ChildClass" WHERE SimpleClass_id=:p0;:p0 = 1
Refresh
DEBUG - SELECT Id FROM "ChildClass" WHERE Id=:p0;:p0 = 1
DEBUG - SELECT Id FROM "ChildClass" WHERE Id=:p0;:p0 = 2
DEBUG - SELECT Id FROM "ChildClass" WHERE Id=:p0;:p0 = 3
DEBUG - SELECT Id FROM "SimpleClass" WHERE Id=:p0;:p0 = 1

Я все еще не уверен в других последствиях этого

1 Ответ

0 голосов
/ 23 марта 2011

Если вы посмотрите на сгенерированный SQL для Refresh, вы увидите, что он загружает дочерние элементы с левым соединением (а не со вторым запросом, поэтому он не загружается лениво), в то время как первая операция Get не имеет объединение

Refresh () использует настройки каскада и загружает коллекции, где для каскада установлено значение «все», поэтому ваша проблема здесь:

HasMany(sc => sc.Childs)
            .Cascade.All()
            .LazyLoad();

Если вы напишите Cascade.None или Cascade.SaveUpdate, он должен получить желаемый результат. Конечно, я не знаю, нужен ли вам Cascade.All. В этом случае я предлагаю заменить Refresh () следующим:

m_session.Evict(simple);
simple = m_session.Get<Order>(1);

Редактировать:

Вы можете проверить состояние инициализации и выполнить обновление в зависимости от этого. Это, вероятно, не самое красивое решение.

class SimpleClassDAL
{
    [...]
    public void Refresh(ISession session, SimpleClass simple)
    {
        var persistentcollection = simple.Childs as IPersistentCollection;
        if (persistentcollection.WasInitialized)
        {
            // get everything again
            session.Refresh(simple);
        }
        else
        {
            // only refresh simple
            session.Evict(simple);
            simple = session.Get<SimpleClass>(simple.Id);

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