StackOverFlowException with Owner relations (ObjectBuilder)

Topics: Web Client Software Factory, User Forum
Apr 26, 2007 at 11:55 AM
For example i've got the following classes

  • ApplicationController (The ApplicationController has an instance of ClientController & InvoiceController, because he's the bridge between the ClientController & InvoiceController and vice versa.)
  • ClientController (ClientController owner is ApplicationController.)
  • InvoiceController (InvoiceController owner is ApplicationController.)

Thus when I'm on an Client-screen and I want to go to an Invoice-screen I'll have to take the bridge(ApplicationController) to go there and vice-versa.

But when I implement the following I get an StackOverFlowException. I've tried everything with the parameters of the DependancyAttribute,
but no luck. I Also tried it with the CreateNewAttribute

 
 
	public class ApplicationController
	{
		private ClientController _clientController;
		private InvoiceController _invoiceController;
 
		[InjectionConstructor]
		public ApplicationController() { }
 
		[Dependency]
		public ClientController ClientController
		{
			set { _clientController = value; }
		}
 
		[Dependency]
		public InvoiceController InvoiceController
		{
			set { _invoiceController = value; }
		}
 
		public void OpenInvoice(int invoiceId)
		{
			_invoiceController.Open(invoiceId);
		}
 
		public void OpenClient(int clientId)
		{
			_clientController.Open(clientId);
		}
	}
 
	public class ClientController
	{
		private ApplicationController _owner;
 
		[InjectionConstructor]
		public ClientController([Dependency] ApplicationController owner)
		{
			_owner = owner;
		}
 
		public void Open(int clientId)
		{
			/// Some code here
		}
 
		public void OpenInvoice(int invoiceId)
		{
			_owner.OpenInvoice(invoiceId);
		}
	}
 
	public class InvoiceController
	{
		private ApplicationController _owner;
 
		[InjectionConstructor]
		public InvoiceController([Dependency] ApplicationController owner)
		{
			_owner = owner;
		}
 
		public void Open(int invoiceId)
		{
			/// Some code here
		}
 
		public void OpenClient(int clientId)
		{
			_owner.OpenClient(clientId);
		}
	}
 

Does somebody know how I can do this. And is there GOOD documentation of the ObjectBuilder?

Thanx in advance.
Apr 27, 2007 at 9:35 AM
Edited Apr 27, 2007 at 9:57 AM
I've finally found an solution. If you register your application controller as
an Module Service in the moduleinitializer then you can get with the following
code an working solution .

Code for moduleinitializer:
 
		protected virtual void AddModuleServices(IServiceCollection moduleServices)
		{
			moduleServices.AddNew<ApplicationController>();
		}
 

Controller code:
 
	public class ApplicationController
	{
		private ClientController _clientController;
		private InvoiceController _invoiceController;
 
		[InjectionConstructor]
		public ApplicationController() { }
 
		[Dependency]
		public ClientController ClientController
		{
			set { _clientController = value; }
		}
 
		[Dependency]
		public InvoiceController InvoiceController
		{
			set { _invoiceController = value; }
		}
 
		public void OpenInvoice(int invoiceId)
		{
			_invoiceController.Open(invoiceId);
		}
 
		public void OpenClient(int clientId)
		{
			_clientController.Open(clientId);
		}
	}
 
	public class ClientController
	{
		private ApplicationController _owner;
 
		[InjectionConstructor]
		public ClientController([ServiceDependency] ApplicationController owner)
		{
			_owner = owner;
		}
 
		public void Open(int clientId)
		{
			/// Some code here
		}
 
		public void OpenInvoice(int invoiceId)
		{
			_owner.OpenInvoice(invoiceId);
		}
	}
 
	public class InvoiceController
	{
		private ApplicationController _owner;
 
		[InjectionConstructor]
		public InvoiceController([ServiceDependency] ApplicationController owner)
		{
			_owner = owner;
		}
 
		public void Open(int invoiceId)
		{
			/// Some code here
		}
 
		public void OpenClient(int clientId)
		{
			_owner.OpenClient(clientId);
		}
	}
 


PS: Sayed Y. Hashimi wrote an excellent article over the ObjectBuilder http://www.sayedhashimi.com/CategoryView,category,ObjectBuilder.aspx
Coordinator
Apr 27, 2007 at 2:10 PM
Hi jchompff,
I wouldn't recommend you to do that, because in that case, the ApplicationController gets initialized once on module initialization, and gets automatically injected with 2 controllers (client and invoice).
Then, when a presenter creates a new ClientController, it would obtain a reference to this instance of ApplicationController, which has a reference to another ClientController, and not this newly created one. Maybe this isn't an issue to you, but be aware of that.
By the way, I see you are registering ApplicationController as a module service. If both Client and Invoice controller are on the same module, couldn't you just set a direct dependency to each other instead of using the ApplicationController?
It would be even better if you create a ClientService with the Open(int clientId) method, and register it as a service, and this service is consumed by both the Client and Invoice controller, also leaving ApplicationController out of the picture. Also register InvoiceService.

Please let me know if this clarify things a little,
Julián Domínguez
http://staff.southworks.net/blogs/jdominguez
May 2, 2007 at 7:35 AM
Julián,

Sorry for the late anwser, but I've had extra-long weekend (almost everybody in the Netherlands, Queensday).

Is the ApplicationController only created once per application or is created for each session. The application controller
is an bridge to other use-cases (pages/pageflows) who handles the stack of usecases, because the pageflow
doesn't work with embedded pageflows.

Greetz,

Joey Chömpff
Coordinator
May 2, 2007 at 7:59 PM
Hi Joey,
In your case, if you register the ApplicationController as a service, it will be created only once, so that would be a problem to you, because in this ApplicationController you are holding references to your other controllers, which shouldn't be valid.
If you need to stick to the ApplicationController idea, wouldn't it be better to not hold references to instances of Invoice and Client controllers, but instead make calls to static methods in these controllers? Or if you need these to be instances, before calling them, create a new instance of the controllers using the standard new operator.

Let me know if this helps,
Julián Domínguez
http://staff.southworks.net/blogs/jdominguez
Jun 28, 2007 at 9:52 AM
Hi Julián,

It's long time ago but my R&D project was on the hold. And I've succesfully migrated to WCSF 1.1 (far more stable ;-) ),
so I've started it up again.

  • Client1 has 2 invoices, 'Invoice 1' and 'Invoice 2'
  • Client1 has 1 invoice, 'Invoice 3'

How would you handle the following navigation paths

  1. Client 1 --> Invoice 1 --> Invoice 2 --> Client 1 (back to)
  2. Client 2 --> Invoice 3 --> Client 2 (back to)

TIA,

Joey Chömpff
http://jchompff.blogspot.com/
Jun 29, 2007 at 5:18 AM
Joey,

Before going into your new question, you normally resolve the original issue by clearing the bidirectional dependecies on your controller's design. The key is to avoid the need of holding references in both places (ApplicationController + ClientController). It isn't normally hard to resolve it by using events (for instance the application controller raises events, instead of directly calling the other controllers ... or vicevesa, is the ClientController which raises an event instead of holding a reference to the applicationcontroller). The problem in there lies in the fact that you would need all the 3 controllers loaded, which would probably mean you need the views loaded. This fact means the solution is just not compatible with the wscf because of the way asp.net works (you only get a page loaded per request, meaning you only have access to a single view).

Now, if the problem you are really trying to solve is the one of that page flow you just mentioned, you can use the page flow funtionality that comes in the last release of the wcsf. The solution is far easier and cleanier since you would be managing that flow in a page flow diagram. What you need is saving the state in the page flow, for example the invoices to be processed (or their IDs depending on your scenario). Asuming you don't need a confirmation at the end and you can process the invoice modifications right away, you just remove the pending invoice from the flow state and call Next on it whenever you finish processing the invoice on the invoice controller. The flow would need a simple check that determines if there are pending invoices, in which case it will continue on the same invoice processing page (the controller will fetch the first pending invoice saved on the page flow state). If the flow determines that aren't any pending invoices left, it will procced to the client page. You would need to decide where to put the complete call of the page flow.

You need to be aware that the current page flow providers configuration requires the use of cookies. If this doesn't work in your scenario, you can look at alternatives to the default provider implementation or implement a service with the page flow logic + state that the controllers use use following the guidelines mentioned before.

I might be missing something additional you are wanting to handle.

Freddy Rios
http://www.gcmfactory.net/en/
Jul 2, 2007 at 7:35 AM
Freddy,

Thanks for your anwser, but your solution is not quite what I want. My application will be more modular the client and the invoices are
packed in their own modules and their pageflows evenso. Only the ApplicationController knows the existence of all the loaded modules.

Joey Chömpff
jchompff.blogspot.com