-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 
Author Message
 Post subject: Lazy loading doesn't call IInterceptor.Instantiate
PostPosted: Thu Feb 15, 2007 1:22 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Hibernate version: 1.2.0beta3

We use the Strategy design pattern to attach customizable business rules classes to our entities. The entities themselves are POCOs and are generated, not hand-written. We attach an appropriate strategy object to the entity from both our own entity factory (when new, transient entities are created) and through an implementation of IInterceptor (for those entities NHibernate loads from the database).

Unfortunately, proxies don't call IInterceptor.Instantiate, but instead call the entity's constructor directly. This is causing us serious problems, because the entity's strategy object never gets attached.

There are numerous posts justifying the lack of support for intercepting proxy initialization, with the argument being that it would encourage people to try to reconnect sessions on demand, e.g.

http://forum.hibernate.org/viewtopic.php?t=965920

But I'm not trying to do that -- I just want a way to get notified when my entities have already beein initialized by NHibernate, so that I can perform my own additional, required initialization that doesn't involve any active session.

Any ideas? Does anyone else use the Strategy pattern this way?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 19, 2007 5:37 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Doesn't anyone else implement IInterceptor.Instantiate? If so, don't you have problems due to NHibnerate inconsistently calling it -- i.e. not calling it if the entity is lazy-loaded? Why would this behavior be desirable?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 20, 2007 4:17 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
I can't see how this can happen from the source. Can you provide a test case or at least describe where in the source is the constructor is called directly?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 20, 2007 3:29 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
If NHibernateUtil.Initialize() is performed on an uninitialized proxy, then IInterceptor.Instantiate gets called properly. However, the proxy calls the constructor directly if the result set of a query is iterated, and lazy-loading occurs.

Here is the stack trace:

Code:
MyCompany.Framework.Entities.dll!MyCompany.Framework.Entities.PluginLayer.PluginLayer() Line 30   C#
DynamicAssemblyProxyGen!CProxyTypeMyCompany_Framework_EntitiesPluginLayerEntities_NHibernate_ProxyINHibernateProxy_System_Runtime_SerializationISerializable2.CProxyTypeMyCompany_Framework_EntitiesPluginLayerEntities_NHibernate_ProxyINHibernateProxy_System_Runtime_SerializationISerializable2() + 0x2b1 bytes   
[Native to Managed Transition]   
[Managed to Native Transition]   
mscorlib.dll!System.RuntimeType.CreateInstanceImpl(System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder = {System.DefaultBinder}, object[] args = {Dimensions:[1]}, System.Globalization.CultureInfo culture, object[] activationAttributes = null) + 0x24d bytes   
mscorlib.dll!System.Activator.CreateInstance(System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, object[] args, System.Globalization.CultureInfo culture, object[] activationAttributes) + 0x73 bytes   
mscorlib.dll!System.Activator.CreateInstance(System.Type type, object[] args) + 0x11 bytes   
Castle.DynamicProxy.dll!Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance(System.Type type = {Name = "CProxyTypeMyCompany_Framework_EntitiesPluginLayerEntities_NHibernate_ProxyINHibernateProxy_System_Runtime_SerializationISerializable2" FullName = "CProxyTypeMyCompany_Framework_EntitiesPluginLayerEntities_NHibernate_ProxyINHibernateProxy_System_Runtime_SerializationISerializable2"}, Castle.DynamicProxy.IInterceptor interceptor = {NHibernate.Proxy.CastleLazyInitializer}, object[] argumentsForConstructor = {Dimensions:[0]}) + 0x71 bytes   
Castle.DynamicProxy.dll!Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(System.Type baseClass = {Name = "PluginLayer" FullName = "MyCompany.Framework.Entities.PluginLayer"}, System.Type[] interfaces = {Dimensions:[1]}, Castle.DynamicProxy.IInterceptor interceptor = {NHibernate.Proxy.CastleLazyInitializer}, bool checkAbstract = false, object[] argumentsForConstructor = {Dimensions:[0]}) + 0x8c bytes   
NHibernate.dll!NHibernate.Proxy.CastleProxyFactory.GetProxy(object id = "BaseApp   ", NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}) + 0xc2 bytes   
NHibernate.dll!NHibernate.Persister.Entity.AbstractEntityPersister.CreateProxy(object id = "BaseApp   ", NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}) + 0x3b bytes   
NHibernate.dll!NHibernate.Impl.SessionImpl.DoLoadByClass(System.Type clazz = {Name = "PluginLayer" FullName = "MyCompany.Framework.Entities.PluginLayer"}, object id = "BaseApp   ", bool checkDeleted = false, bool allowProxyCreation = true) + 0x264 bytes   
NHibernate.dll!NHibernate.Impl.SessionImpl.InternalLoad(System.Type clazz = {Name = "PluginLayer" FullName = "MyCompany.Framework.Entities.PluginLayer"}, object id = "BaseApp   ", bool eager = false, bool isNullable = false) + 0x6d bytes   
NHibernate.dll!NHibernate.Type.EntityType.ResolveIdentifier(object id = "BaseApp   ", NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}) + 0x64 bytes   
NHibernate.dll!NHibernate.Type.EntityType.ResolveIdentifier(object id = "BaseApp   ", NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}, object owner = {MyCompany.Framework.Entities.PluginRegistryEntryLayer}) + 0x71 bytes   
NHibernate.dll!NHibernate.Impl.SessionImpl.InitializeEntity(object obj = {MyCompany.Framework.Entities.PluginRegistryEntryLayer}) + 0x19b bytes   
NHibernate.dll!NHibernate.Loader.Loader.InitializeEntitiesAndCollections(System.Collections.IList hydratedObjects = Count = 2, object resultSetId = {NHibernate.Driver.NHybridDataReader}, NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}) + 0x164 bytes   
NHibernate.dll!NHibernate.Loader.Loader.DoQuery(NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}, NHibernate.Engine.QueryParameters queryParameters = {NHibernate.Engine.QueryParameters}, bool returnProxies = true) + 0x38a bytes   
NHibernate.dll!NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}, NHibernate.Engine.QueryParameters queryParameters = {NHibernate.Engine.QueryParameters}, bool returnProxies = true) + 0x56 bytes   
NHibernate.dll!NHibernate.Loader.Loader.LoadCollection(NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}, object id = "Y8BVX9QYXF", NHibernate.Type.IType type = {NHibernate.Type.StringType}) + 0x117 bytes   
NHibernate.dll!NHibernate.Loader.Collection.CollectionLoader.Initialize(object id = "Y8BVX9QYXF", NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}) + 0x4a bytes   
NHibernate.dll!NHibernate.Persister.Collection.AbstractCollectionPersister.Initialize(object key = "Y8BVX9QYXF", NHibernate.Engine.ISessionImplementor session = {NHibernate.Impl.SessionImpl}) + 0x4c bytes   
NHibernate.dll!NHibernate.Impl.SessionImpl.InitializeCollection(NHibernate.Collection.IPersistentCollection collection = {NHibernate.Collection.PersistentBag}, bool writing = false) + 0x1e4 bytes   
NHibernate.dll!NHibernate.Collection.AbstractPersistentCollection.Initialize(bool writing = false) + 0xe5 bytes   
NHibernate.dll!NHibernate.Collection.AbstractPersistentCollection.Read() + 0x2d bytes   
NHibernate.dll!NHibernate.Collection.PersistentBag.GetEnumerator() + 0x2d bytes


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 21, 2007 2:59 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Well, yes, the proxy constructor calls the default constructor when the proxy is being created, since the proxy class is derived from the entity class. However, when the proxy is later initialized (i.e. the real object is being created), Instantiate should still be called.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 21, 2007 6:18 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Hmmm... the particular problem I ran into was that my entity overrides ToString(), which calls a method on the entity's associated "Strategy" object. When ToString() gets called on an uninitialized proxy, it ends up calling my overriden implementation, but IInterceptor.Instantiate isn't called first (the proxy doesn't get initialized?), and my ToString() implementation fails with a null object reference.

In any case, if IInterceptor.Instantiate does get called by a proxy as it initializes, yet it calls my entity's constructor directly before doing so, what should I be doing in IInterceptor.Instantiate to get the actual entity instance that the proxy just created? No session is passed, so I can't look it up by the id passed. I don't see how this would work, or why the proxy would call IInterceptor.Instantiate if it already called my entity's constructor. What would the proxy do with an instance I might return from IInterceptor.Instantiate?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 21, 2007 10:13 pm 
Beginner
Beginner

Joined: Thu Nov 02, 2006 5:11 pm
Posts: 32
Location: Toronto
I think what sergey is saying is that there are two object instances to consider here: the proxy object, which is a subclass of your class, and then the inner object, which is an actual instance of your class.

Because the proxy is a subclass of your class, the constructor of your class will be invoked when the proxy is created. However, you don't need to care about this.

When the proxy is initialized, it creates the inner object, and for this it should ask IInterceptor.Instantiate for an instance of your class.

Please let me know if you get this sorted out, because I need to do something similar and I'll be in trouble if this doesn't work.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 22, 2007 11:49 am 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
jr76, I believe your description is correct. I shouldn't need to care about the proxy instance calling my entity's constructor directly due to inheritance. The problem then seems to be that the proxy doesn't know to initialize when ToString() is called, or it has been coded specifically not to initialize in that case in order to benefit some people but screw up my situation ...


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 22, 2007 12:17 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
No, ToString isn't handled specially in any way. The only methods that are handled specially are Equals/GetHashCode, Finalize (i.e. a finalizer) and GetObjectData (i.e. serialization). A stupid question - your ToString override isn't sealed, is it?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 27, 2007 5:51 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
My ToString() method looks like this:
Code:
public override string ToString()
{
    return this.Strategy.Label;
}


where the Strategy property needs to get assigned via IInterceptor.Instantiate (and Label is an overridable string property on the strategy class).

I did notice that our Strategy property is not virtual, and I'll change it so it is (there could be cases where the first access on an entity is its strategy property), but in this case, I thought calling ToString() would have caused the proxy to get initialized.

I'll get you a stack trace for the ToString() scenario ...


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.