View Full Version : Non-singleton objects and circular references
andoband
05-01-2005, 01:58 AM
Is it possible to have a simple bidirectional (circular) reference for non-singleton objects?
For example:
<object id="objA" type="Scenic.Orchestration.Tests.ClassA, Scenic.Orchestration.Tests" singleton="false">
<property name="ClassB"><ref local="objB"/></property>
</object>
<object id="objB" type="Scenic.Orchestration.Tests.ClassB,Scenic.Orchestra tion.Tests" singleton="false">
<property name="ClassA"><ref local="objA"/></property>
</object>
Currently this gives me a stack overflow exception when I try to retrieve the object from the context.
-Andy
Mark Pollack
05-02-2005, 01:28 AM
Hi Andy,
Thanks for reporting the stackoverflow - I'll look into that.
Meanwhile, there are a couple of approaches you can take. The easiest of which would be to remove the circular dependency if possible :). Aside from OO type reasons to do this you would run into trouble compiling if the classes were in different assemblies. Also, if you don't think that you are going to set any other properties on objA or objB, you can just stick a 'new' inside each of their constructors and set the references programmatically.
Alternatively, I've created an implementation of IObjectPostProcessor (http://www.springframework.net/doc/reference/html/objects.html#objects-factory-customizing) that loosely mimics what you would be doing in code - creating both instances and then setting property references. Minimally this would be
ClassA ca = new ClassA();
ClassB cb = new ClassB();
ca.ClassB = cb;
cb.ClassA = ca;
In the application context declaration this translates to
<object name="objA" type="circref.ClassA, circref" singleton="false">
</object>
<object name="objB" type="circref.ClassB, circref" singleton="false">
</object>
<object name="circRefPostProcessor" type="circref.CircularReferenceObjectPostProcessor">
<property name="firstObjectName"><value>objB</value></property>
<property name="firstObjectPropertyName"><value>ClassA</value></property>
<property name="secondObjectName"><value>objA</value></property>
<property name="secondObjectPropertyName"><value>ClassB</value></property>
</object>
The post processor is automaticaly registered and is called after an object is instantiated and all properties are set. The post processor is called for all objects declared in the context.
The CircularReferenceObjectPostProcessor checks if the object being created is one of the names specified by the firstObjectName or secondObjectName properties. It then creates the dependent object and sets the property that would cause a circular dependency. It breaks the circular dependency by keeping track of the objects it is creating. Note that the references inside the object definition that cause circular dependencies need to be removed.
You can pick up the code on the wiki. (http://opensource.atlassian.com/confluence/spring/display/NET/Forum+Questions)
Give it a whirl and let me know how it goes. If you have many circular dependencies per object pair an improvement would be to have a list of names for each property in CircularReferenceObjectPostProcessor.
Cheers,
Mark
andoband
05-02-2005, 07:59 PM
Thanks Mark! I'll give it a whirl.
As far as using the cyclic references go, I don't generally use them. But lately i've been playing around with nHibernate, and there's a lot of talk about bi-directional relationships from that camp. So i've tried it in a few places to get a feel for it. Given the way i'm used to writting code, it's really only complicated things.
BTW, I'm blown away by spring and it's potential. Great work.
-andy
Mark Pollack
05-02-2005, 09:07 PM
Hi,
Gotya. BTW, I typically don't use Spring to create the domain objects for use with hibernate - I guess I never felt the need. I handle bidirectional relationships inside the domain object itself. For example a user can be assigned one or more roles. The Role object has a collection of users and the User object has a collection of roles. The Role object has a method 'AddUser' that sets the references
public void AddUser(User u)
{
if (u == null) throw ...
u.Roles.Add(this);
Users.Add(u)
}
or something like that. Then I just do a hibernate update on the role. Removing is similar. Just my 2 cents on hibernate usage...
Cheers,
Mark
andoband
05-02-2005, 09:16 PM
yeah, i've got the Add<class> methods as well.
I'm using spring to populate domain objects for unit tests...some of which CRUD those domain objects to the db in order to testmy hbm.xml files. Does that approach throw up any red flags to you?
Mark Pollack
05-02-2005, 09:30 PM
Hi,
Hmm no red flags. If it works for you go with it, though if you have the add methods in the style listed above there should be no need to have spring set the circular references. In my tests I have a 'fixture class' that has a bunch of convenience methods for getting at various DAO objects (pulling from spring underneath) as well as creating some preconfigured domain objects (raw 'new' underneath). The domain object related methods are typically parameterized and sometimes even use some DAOs. I know some spring.java guys that load a context per unit test - the context file name is derived from the class name. I suppose there are many valid approaches - testing at the functional level is always a bit of an art. BTW, using any code coverage tool?
Mark
vBulletin® v3.7.3, Copyright ©2000-2008, Jelsoft Enterprises Ltd.