WCF Wire 上的 NHibernate Lazy 初始化集合

发布于 2024-08-22 07:12:14 字数 733 浏览 4 评论 0原文

我的对象看起来像这样:

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 异常

My object looks something like this:

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

All the fields are getting populated from the database and the collection is getting lazy initialization which is desirable.
Now, my problem is that I want to send this object to the web service. But since the collection is lazily loaded, am not able to do it. Can somebody please give me an idea or a direction or some example code which I can look into for my problem.

I want a generic way to force initialization for this list before I send it over the wire. Also, I have multiple objects like this so a generic way to do it would be great. Right now, to do this I am using this method:

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

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

But this throws an object reference set to null exception for some reason

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

我做我的改变 2024-08-29 07:12:14

没有什么灵丹妙药可以让收藏变得“放松”。您很可能会触发 SELECT+1 问题。您确实需要对对象图执行急切加载。

可以在映射中执行此操作,但我建议您将映射中的所有内容保留为惰性。最好的策略是在从数据库获取数据时覆盖此行为。如果使用 HQL,查询类似于“

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

如果您使用 ICriteria,请使用 SetFetchMode()”。

当您执行 List() 时,您会注意到重复的根实体对象。这是预期的行为。如果这不是您想要的,最简单的解决方案是将所有内容加载到 HashedSet 中并枚举结果。

注意:每个实体只能急切加载一个集合。这是 SQL 的限制。

There is no magic bullet to 'unlazy' a collection. You would most likely trigger the SELECT+1 problem. You really need to perform an eager load on object graph.

You can do this in your mappings, but I recommend that you leave everything in your mappings as lazy. The best policy is to override this behavior when you fetch from the database. If using HQL, the query is something like

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

If you're using ICriteria, use SetFetchMode().

You will notice duplicate root entity objects when you perform List(). This is expected behavior. If this is not what you want, the easiest solution is to load everything into a HashedSet and enumerate the results.

N.B. You can only eagerly load one collection per entity. This is a limitation of SQL.

很糊涂小朋友 2024-08-29 07:12:14

http: //trentaulous.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();
    }

祝你好运 =)

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

by the way, if you probably want set your proxy collections = null, do follow:

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

Good luck =)

苍景流年 2024-08-29 07:12:14

我不确定我是否正确理解了这个问题。您在服务器端有一个数据库 - 您可以将 NHibernate 与该数据库一起使用 - 使用延迟加载。当您的对象传输到客户端时遇到问题吗?

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

希望这有帮助。

I'm not sure if I understood the question correctly. You have a database on the server side - which you use NHibernate with - using lazy loading. The you get problems with your objects when transdered to the client side?

If this is the case what you need to do is stop lazy loading. As the client doesn't have access to the Database you won't be able to load the lazy loaded data from the client side. You need to make sure the data is loaded on the server side before transfering it to the client. The simplest way of solving this is to stop lazy loading. If not you need to ensure the lazy data is loaded before the holding object is returned to the client. If you have a service that fetches data from the database, does something, and returns this data you might just as well load your data from the database right away.

Hope this helps.

从来不烧饼 2024-08-29 07:12:14

简短的回答:虽然你可能会因为必须比你想要的更多地握住 NHibernate 的手而感到痛苦(就像它对我一样),但我同意 James L 的观点,即急切的获取似乎是最好的方法。

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

因此,我们成功地使用 Automapper 将实体转换为相应的 DTO,以便在与 WCF 连线。这样做的一个副作用是,它基本上不会延迟所有事情,因为它将遍历对象图以便将其放入另一个对象中。

虽然这可行,但我必须强烈同意 James 关于选择 N+1 问题的警告;我们在网络应用程序中发现,单个页面加载可能会导致数百个单独的数据库调用。首先通过 HQL 或 Criteria 进行急切获取,确实可以减少大量数据。

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

我不确定这是真的,但我当然建议您在单独的查询中加载集合;我认为您可以将它们全部加载到一个查询中,但在底层 SQL 中,它们将保留为外连接,这将导致笛卡尔积很容易失控。

像往常一样,Ayende Rahien 有 已经很好地描述了所有这些,以及如何使用 Futures 至少在一次往返中相对轻松地完成所有这些单独的集合查询的描述。

Short answer: though it may pain you (as it did me) to have to hold NHibernate's hand more than you wanted to, I concur with James L that eager fetching seems to be the best way to do it.

As per Mauricio's links, we've found that passing NHibernate entities across WCF directly doesn't work too well anyway, since you're not really passing objects the POCOs that WCF loves, but proxied objects with NHibernate-specific collections.

For that reason we've had success with using Automapper to convert an entity to a corresponding DTO for passing across the wire with WCF. A side-effect of this is that it basically un-lazies everything simply because it's going to traverse the object graph in order to put it in another object.

Though that works, I have to strongly agree with James' warning regarding the select N+1 problem; we've found in our web app that a single page load can result in hundreds of individual database calls. You can really cut a huge number out by using eager fetching with HQL or Criteria first.

You can only eagerly load one collection per entity. This is a limitation of SQL.

I'm not sure this is true, but I would certainly recommend that you load the collections in separate queries; I think you can load them all in one query, but in the underlying SQL they're going to be left outer joins which will result in a Cartesian product that can very easily get out of control.

As usual, Ayende Rahien has described all of this very well already, as well as description of how to use Futures to at least do all of those separate collection queries in one round-trip with relative ease.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文