WCF Wire上的NHibernate Lazy Initialized集合

时间:2010-02-22 17:00:09

标签: nhibernate lazy-loading collections

我的对象看起来像这样:

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

所有字段都从数据库中填充,并且集合正在进行延迟初始化,这是可取的。 现在,我的问题是我想将此对象发送到Web服务。但由于该系列是懒散的,我无法做到。有人可以给我一个想法或方向或一些示例代码,我可以查看我的问题。

在通过网络发送之前,我想要一种强制初始化此列表的通用方法。此外,我有这样的多个对象,所以这样做的通用方法会很棒。现在,要做到这一点,我使用这种方法:

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

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

但是由于某种原因,这会将对象引用设置为null异常

4 个答案:

答案 0 :(得分:2)

没有灵丹妙药可以让'unlazy'成为一个集合。您很可能会触发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 :(得分:1)

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();
    }

祝你好运=)

答案 2 :(得分:0)

我不确定我是否正确理解了这个问题。你有一个服务器端的数据库 - 你使用NHibernate - 使用延迟加载。转移到客户端时,您的对象出现问题?

如果是这种情况,您需要做的是停止延迟加载。由于客户端无权访问数据库,因此无法从客户端加载延迟加载的数据。在将数据传输到客户端之前,您需要确保在服务器端加载数据。解决这个问题的最简单方法是停止延迟加载。如果不是,则需要确保在将保持对象返回到客户端之前加载了惰性数据。如果您有一个从数据库中获取数据的服务,执行某些操作并返回此数据,您也可以立即从数据库中加载数据。

希望这会有所帮助。

答案 3 :(得分:0)

简短的回答:尽管你可能比你想要的更多地让NHibernate的手感到痛苦(因为它让我感到痛苦),但我同意James L的观点,即渴望获取似乎是最好的方法。

根据Mauricio的链接,我们发现直接在WCF上传递NHibernate实体并不能很好地工作,因为你并没有真正传递WCF喜欢的POCO对象,而是使用NHibernate特定集合的代理对象。

出于这个原因,我们已成功使用Automapper将实体转换为相应的DTO,以便通过WCF传递。这样做的副作用是,它基本上不会让一切变得松懈,因为它会遍历对象图,以便将它放在另一个对象中。

虽然这有效,但我必须非常同意詹姆斯关于选择N + 1问题的警告;我们在我们的网络应用程序中发现,单个页面加载可能导致数百个单独的数据库调用。通过首先使用HQL或Criteria进行预先获取,您可以真正减少数量。

  

您只能急切地为每个实体加载一个集合。这是SQL的限制。

我不确定这是否属实,但我肯定会建议您在单独的查询中加载集合;我认为你可以在一个查询中加载它们,但是在底层SQL中它们将被留下外连接,这将导致笛卡尔积非常容易失控。

像往常一样,Ayende Rahien有described all of this very well already,以及如何使用Futures至少在一次往返中相对轻松地完成所有这些单独的收集查询的描述。