Ленивая загрузка с помощью nHibernate Fluent, веб-сервисов и автомаппера

Я отделяю логику своего домена от логики моего веб-сервиса

Это из моего домена и на самом деле получает данные из nHibernate.

public static IList<Location> LoadReturnLocationsFromDatabase(DateTime lastUpdateTime)
{
    using (var session = NHibernateHelper.OpenSession())
    {
        // retreive all stores and display them
        using (session.BeginTransaction())
        {
            var locations = session.CreateCriteria(typeof(Location)).Add(Expression.Gt("LastUpdatedTime", lastUpdateTime)).SetMaxResults(10).List<Location>();
            return locations;
        }
    }
}

Затем эти данные возвращаются в веб-службу, и я использую Automapper для их дублирования, чтобы не подвергать объект доступа к базе данных веб-службе и хранить все отдельно.

public IList<GetLocationDetailsResponse> GetLocationUpdate(DateTime lastUpdateTimeDT)
{

    Mapper.CreateMap<Location, GetLocationDetailsResponse>();

    IList<Location> locations = WhygoDomain.GetLocations.LoadReturnLocationsFromDatabase(lastUpdateTimeDT);

    IList<GetLocationDetailsResponse> getLocationDetails = Mapper.Map<IList<Location>, IList<GetLocationDetailsResponse>>(locations);
    return getLocationDetails;
}

Моя проблема в том, что я не могу выполнить сопоставление, если не укажу, что связь между местоположением и состоянием не загружается лениво, потому что веб-служба находится снаружи:

using (var session = NHibernateHelper.OpenSession())

в области данных.

Ленивая загрузка кажется предпочтительным методом выполнения чего-то подобного, поэтому мне интересно, подходит ли этот подход? Это служба экспорта данных, которая будет экспортировать, поэтому использование памяти и т. д. может оказаться проблематичным.

Если мне нужно изменить это, является ли причиной проблемы структура моего кода? Если да, то как я могу отделить логику своего домена и обойти эту проблему?


person iKode    schedule 24.01.2012    source источник
comment
Есть ли у вас структура АОП?   -  person Rich O'Kelly    schedule 24.01.2012


Ответы (2)


Нетерпеливый поиск

Вы можете избежать этой проблемы и повысить производительность, активно извлекая состояния вместе с местоположениями, иначе возникнет так называемая проблема "выбрать N+1". См. блог Айенде для хорошего объяснения этого: http://ayende.com/blog/1328/combating-the-select-n-1-problem-in-nhibernate.

По сути, каждый раз при обращении к другому location.State выполняется отдельный SQL-запрос, что в вашем случае может означать до 11 циклов обращения к базе данных. Если бы запрос местоположения включал состояния в LEFT OUTER JOIN, то все необходимые данные можно было бы получить за один цикл обращения к базе данных.

В вашем случае следующий запрос, вероятно, будет работать лучше:

var locations = session.CreateCriteria(typeof(Location))
    .Add(Expression.Gt("LastUpdatedTime", lastUpdateTime))
    .SetMaxResults(10)
    .SetFetchMode("State", FetchMode.Eager)
    .List<Location>();

Инверсия зависимостей

Проблема, с которой вы столкнулись, указывает на тот факт, что GetLocations недостаточно осведомлен о ситуации, чтобы нести ответственность за создание и уничтожение сеансов NHibernate. Создание сеанса NHibernate необходимо переместить как минимум на один уровень выше. Конечно, есть более элегантные способы сделать все это, например, с помощью контейнера IoC, но вот несколько быстрых и грязных кодов, чтобы проиллюстрировать, что я имею в виду:

public IList<GetLocationDetailsResponse> GetLocationUpdate(DateTime lastUpdateTimeDT)
{
    using (var session = NHibernateHelper.OpenSession())
    {
        var locations = GetLocations.LoadReturnLocationsFromDatabase(session, lastUpdateTimeDT);
        return Mapper.Map<IList<Location>, IList<GetLocationDetailsResponse>>(locations);
    }
}

И последнее замечание: Mapper.CreateMap AutoMapper — это статический код установки, который необходимо выполнить только один раз при запуске приложения. Global.asax — лучшее место для такого типа кода.

person Daniel Schilling    schedule 24.01.2012

Поскольку вам нужен открытый сеанс, чтобы иметь возможность сопоставлять присоединенный объект домена с отсоединенным автоматически сопоставляемым объектом, вам необходимо убедиться, что сеанс открыт во время автоматического сопоставления. Вы можете рассмотреть возможность переноса оператора using в вызов веб-службы, а не в метод предметной области.

person Nick Ryan    schedule 24.01.2012
comment
Я знаю, о чем вы говорите, но, похоже, это избавляет от всего смысла разделения веб-службы и домена... Хотя, может быть, я уже слишком разделил его... - person iKode; 24.01.2012
comment
Интересно, будет ли это предпочтительнее не ленивой загрузки данных - person iKode; 24.01.2012
comment
@iKode: Нет смысла лениво загружать данные, если вы все равно получаете доступ ко всем их свойствам сразу после их загрузки. - person Daniel Hilgarth; 24.01.2012
comment
@Дэниел. Да, он мог бы с нетерпением ждать, если бы данные, которые он искал, всегда нуждались в гидратации каждый раз, когда касались объекта верхнего уровня. Зависит от варианта использования. - person Nick Ryan; 24.01.2012