NHibernate Lazy Инициализированная коллекция на WCF Wire - PullRequest
1 голос
/ 22 февраля 2010

Мой объект выглядит примерно так:

class
{
    int a;
    object b;
    IList<string> c;
}

Все поля заполняются из базы данных, а коллекция получает отложенную инициализацию, что желательно. Теперь моя проблема в том, что я хочу отправить этот объект в веб-сервис. Но так как коллекция загружается лениво, я не могу это сделать. Может кто-нибудь, пожалуйста, дайте мне идею или направление или пример кода, который я могу найти для моей проблемы.

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

public static T Unlazy<T>(this T persistentCollection)
{

    if (persistentCollection is IPersistentCollection) 
    { 
        IPersistentCollection collection = (IPersistentCollection)persistentCollection;             
        collection.SetCurrentSession(session.GetSessionImplementation()); 
        collection.ForceInitialization(); 
    } 
}  

Но по какой-то причине это приводит к тому, что для ссылки на объект устанавливается нулевое исключение

Ответы [ 4 ]

2 голосов
/ 22 февраля 2010

Не существует волшебной пули, которая бы «расшатывала» коллекцию. Скорее всего, вы вызовете проблему SELECT + 1. Вы действительно должны выполнить энергичную нагрузку на граф объектов.

Вы можете сделать это в своих отображениях, но я рекомендую оставить все в ваших отображениях как ленивое. Лучшая политика - переопределить это поведение при получении из базы данных. При использовании HQL запрос выглядит примерно так:

"from class 
left join **fetch** class.b b
left join **fetch** class.c c"

Если вы используете ICriteria, используйте SetFetchMode ().

При выполнении функции List () вы заметите дублирующиеся корневые объекты. Это ожидаемое поведение. Если это не то, что вам нужно, самое простое решение - загрузить все в HashedSet и перечислить результаты.

N.B. Вы можете только охотно загрузить одну коллекцию для каждой сущности. Это ограничение SQL.

1 голос
/ 27 января 2011

http://trentacular.com/2009/08/how-to-use-nhibernate-lazy-initializing-proxies-with-web-services-or-wcf/

кстати, если вы, вероятно, хотите установить свои прокси-коллекции = null, выполните следующее:

    public static T UnproxyObjectTreeWithNulls<T>(this T persistentObject, ISessionFactory sessionFactory)  //  Force Null Initialization
    {
        //  Получаем настоящий (не proxy) тип элемента
        var persistentType = persistentObject.GetUnproxiedType();

        if (persistentType == null)     //  persistentObject is IPersistentCollection
            //return persistentObject.UnproxyCollectionObjectTreeWithNulls(sessionFactory);
            return persistentObject;

        //  Получаем NHibernate-метаданные класса
        var classMetadata = sessionFactory.GetClassMetadata(persistentType);

        // Iterate through each property and unproxy entity types
        for (int i = 0; i < classMetadata.PropertyTypes.Length; i++)
        {
            var nhType = classMetadata.PropertyTypes[i];
            var propertyName = classMetadata.PropertyNames[i];
            var propertyInfo = persistentType.GetProperty(propertyName);

            if (nhType.IsCollectionType)
            {
                var propertyValue = propertyInfo.GetValue(persistentObject, null);
                if (propertyValue == null) continue;    //  пропускаем пустые свойства

                if (propertyValue is IPersistentCollection)
                {
                    propertyInfo.SetValue(persistentObject, null, null);
                }
                else
                    propertyValue.UnproxyCollectionObjectTreeWithNulls(sessionFactory);
            }
            else if (nhType.IsEntityType)
            {
                var propertyValue = propertyInfo.GetValue(persistentObject, null);
                if (propertyValue == null) continue;    //  пропускаем пустые свойства
                if (propertyValue is INHibernateProxy)
                    //  Если это прокси объект, то мы зануляем его (и отменяем lazy-загрузку)
                    propertyInfo.SetValue(persistentObject, null, null);
                else
                    //  Иначе идем дальше по графу и зануляем поля внутри него
                    propertyInfo.SetValue(persistentObject, propertyValue.UnproxyObjectTreeWithNulls(sessionFactory), null);
            }   
        }

        return persistentObject;
    }

    /// <summary>
    /// Инициализируем ненужные proxy-коллекции null'ами
    /// </summary>
    public static T UnproxyCollectionObjectTreeWithNulls<T>(this T persistentCollection, ISessionFactory sessionFactory) 
    {
        if (!(persistentCollection is IPersistentCollection))      //  если это IPersistentCollection, то мы должны занулить её
        {
            var c = persistentCollection as System.Collections.ICollection;
            if (c == null) return persistentCollection;

            foreach (var item in c)
                item.UnproxyObjectTreeWithNulls(sessionFactory);    //  проделываем все тоже самое внутри элементов коллекции (а кто сказал что будет легко?)
        }

        return persistentCollection;
    }

    /// <summary>
    /// Gets the underlying class type of a persistent object that may be proxied
    /// </summary>
    public static Type GetUnproxiedType<T>(this T persistentObject)
    {
        var proxy = persistentObject as INHibernateProxy;

        if (proxy != null)
            return proxy.HibernateLazyInitializer.PersistentClass;

        var proxyCollection = persistentObject as IPersistentCollection;
        if (proxyCollection != null)
            return null;

        return persistentObject.GetType();
    }

Удачи =)

0 голосов
/ 21 июля 2010

Краткий ответ: хотя вам (как и мне) может быть больно держать руку NHibernate больше, чем вы хотели, я согласен с Джеймсом Л., что нетерпеливое получение, похоже, лучший способ сделать это.

Что касается ссылок Маурисио, мы обнаружили, что передача сущностей NHibernate через WCF в любом случае работает не слишком хорошо, так как вы на самом деле не передаете объекты POCO, которые любит WCF, а передаете объекты с коллекциями, специфичными для NHibernate.

По этой причине мы успешно использовали Automapper для преобразования сущности в соответствующий DTO для передачи по проводам с WCF. Побочным эффектом этого является то, что он в основном все ленится просто потому, что собирается пересечь граф объекта, чтобы поместить его в другой объект.

Хотя это работает, я должен полностью согласиться с предупреждением Джеймса относительно проблемы выбора N + 1; В нашем веб-приложении мы обнаружили, что загрузка одной страницы может привести к сотням отдельных обращений к базе данных. Вы действительно можете вырезать огромное количество, сначала воспользовавшись энергичной выборкой с помощью HQL или Criteria.

Вы можете загружать только одну коллекцию для каждой сущности. Это ограничение SQL.

Я не уверен, что это правда, но я бы определенно рекомендовал вам загружать коллекции в отдельных запросах; Я думаю, что вы можете загрузить их все в одном запросе, но в базовом SQL они останутся внешними объединениями, что приведет к декартовому продукту, который очень легко выйдет из-под контроля.

Как обычно, Ayende Rahien уже описал все это очень хорошо уже , а также описание того, как использовать Futures, чтобы по крайней мере с относительной легкостью выполнить все эти отдельные запросы коллекции за один прием в оба конца. .

0 голосов
/ 22 февраля 2010

Я не уверен, правильно ли я понял вопрос. У вас есть база данных на стороне сервера - с которой вы используете NHibernate - используя ленивую загрузку. Вы получаете проблемы с вашими объектами при передаче на клиентскую сторону?

Если это так, то вам нужно прекратить ленивую загрузку. Поскольку клиент не имеет доступа к базе данных, вы не сможете загружать лениво загруженные данные со стороны клиента. Необходимо убедиться, что данные загружены на стороне сервера, прежде чем передавать их клиенту. Самый простой способ решить эту проблему - остановить ленивую загрузку. Если нет, вам нужно убедиться, что ленивые данные загружены до того, как объект удержания возвращается клиенту. Если у вас есть служба, которая извлекает данные из базы данных, что-то делает и возвращает эти данные, вы можете сразу же загрузить свои данные из базы данных.

Надеюсь, это поможет.

...