PDA

View Full Version : Control global dependency injection


lahma
08-29-2005, 11:20 AM
I have great trouble getting global injection working for user controls. Here's my (obfuscated, unnecessary parts ripped) configuration. This all works as expected (many pages, objects etc with dependencies, AOP) excluding my attempts with Controls. My controls and pages correctly inherit from Spring versions of page and control.

//
// spring-objects.xml
//
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns.... >
<object id="userManagement" parent="baseBusinessObject">
<property name="target">
<object type="MyProject.UserManagement, MyProject">
</object>
</property>
</object>
.....
</objects>
//
// spring-pages.xml
//
...
<objects>
....
<object type="Project.Controls.MyControl" >
<property name="UserManagement">
<ref object="userManagement" />
</property>
</object>
</objects>

Both definitions are included in web.config:
<spring>
<context type="Spring.Context.Support.WebApplicationContext, Spring.Web">
<resource uri="~/Config/spring-objects.xml"/>
<resource uri="~/Config/spring-pages.xml"/>
</context>
</spring>

I tried to debug this and for me it seems that there is some kind of infinite recursion or iteration with finding my control and then finding userManager. UserManager actually is AOP proxy (from parent) and it has DAO dependency (part of the endles loop). What I get from debugger it seems that control is found correctly (run-time type info etc) but the injection just keeps going. IIS freezed and breaking run leads to AbstractAutowireCapableObjectFactory.CreateObject with it looping with my Control, UserManagement and UserDao. I haven't tried with local Controls because this one is really used globally and with same dependencies.

Is this global injection supposed to work? Any help greatly appreciated. Thanks for your great work on this great framework!

Cheers,
Marko Lahma

Ted Husted
08-31-2005, 04:52 PM
Have you had any luck with this, lahma?

I may be trying control injection later today, and will let you know how it turns out.

I think global injection issuppose to work. At least, it's documented here as working

* http://opensource2.atlassian.com/confluence/spring/display/NET/Dependency+Injection+for+ASP.Net+pages

I think I'd be using both global and local in my own work.

-Ted.

lahma
08-31-2005, 08:30 PM
No luck so far. I got quite frustrated trying to get it to work. Maybe you'll be kind enough to share some minimal configuration (but more complete than the one in Confluence) if you get the magic done and I'll do some diffing between configs.

-Marko

Aleks Seovic
09-02-2005, 12:37 AM
I think I see what the problem is in your config file...

Try changing definition for your control from


<object type="Project.Controls.MyControl" >
<property name="UserManagement">
<ref object="userManagement" />
</property>
</object>


to


<object id="Project.Controls.MyControl">
<property name="UserManagement">
<ref object="userManagement" />
</property>
</object>


Because Spring never instantiates controls directly it is not necessary (nor recommended) to specify the type -- instead, full type name should be used as an *id* of the control injection template.

Similarily, there is no reason to specify type for locally injected controls either. All you need to do is set id attribute to a UniqueID of the control.

Let me know if this helped,

Aleks

lahma
09-02-2005, 03:23 PM
Hi again,

Spring isn't very happy when I leave the type part undefined:

[TypeLoadException: Could not load type from string value '']
Spring.Util.TypeResolver.ValidateTypeName(String typeName) in D:\Work\Spring.Net\src\Spring\Spring.Core\Util\Typ eResolver.cs:117
Spring.Util.CachedTypeResolver.Resolve(String typeName) in D:\Work\Spring.Net\src\Spring\Spring.Core\Util\Cac hedTypeResolver.cs:80
Spring.Objects.ObjectUtils.ResolveType(String typeName) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ ObjectUtils.cs:301
Spring.Objects.Factory.Support.SimpleInstantiation Strategy.Instantiate(RootObjectDefinition definition, String name, IObjectFactory factory) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ Factory\Support\SimpleInstantiationStrategy.cs:110
Spring.Objects.Factory.Support.WebInstantiationStr ategy.Instantiate(RootObjectDefinition definition, String name, IObjectFactory factory) in D:\Work\Spring.Net\src\Spring\Spring.Web\Core\Obje cts\Factory\Support\WebInstantiationStrategy.cs:79
Spring.Objects.Factory.Support.AbstractAutowireCap ableObjectFactory.CreateObject(String name, RootObjectDefinition definition, Object[] arguments, Boolean allowEagerCaching) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ Factory\Support\AbstractAutowireCapableObjectFacto ry.cs:723

[ObjectCreationException: Error creating object with name 'MyProject.Controls.MyWebControl' defined in 'file [D:\Work\MyProject\Source\Project.Web\Config\spring-pages.xml] line 141' : Initialization of object failed : Could not load type from string value '']
Spring.Objects.Factory.Support.AbstractAutowireCap ableObjectFactory.CreateObject(String name, RootObjectDefinition definition, Object[] arguments, Boolean allowEagerCaching) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ Factory\Support\AbstractAutowireCapableObjectFacto ry.cs:789
Spring.Objects.Factory.Support.AbstractAutowireCap ableObjectFactory.CreateObject(String name, RootObjectDefinition definition, Object[] arguments) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ Factory\Support\AbstractAutowireCapableObjectFacto ry.cs:634
Spring.Objects.Factory.Support.WebObjectFactory.Cr eateAndCacheSingletonInstance(String objectName, RootObjectDefinition objectDefinition, Object[] arguments) in D:\Work\Spring.Net\src\Spring\Spring.Web\Core\Obje cts\Factory\Support\WebObjectFactory.cs:151
Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObject(String name, Type requiredType, Object[] arguments) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ Factory\Support\AbstractObjectFactory.cs:210
Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObject(String name, Object[] arguments) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ Factory\Support\AbstractObjectFactory.cs:1195
Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObject(String name) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ Factory\Support\AbstractObjectFactory.cs:1156
Spring.Objects.Factory.Support.DefaultListableObje ctFactory.PreInstantiateSingletons() in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ Factory\Support\DefaultListableObjectFactory.cs:40 9
Spring.Context.Support.AbstractApplicationContext. Refresh() in D:\Work\Spring.Net\src\Spring\Spring.Core\Context\ Support\AbstractApplicationContext.cs:703
Spring.Context.Support.WebApplicationContext..ctor (String[] configurationLocations) in D:\Work\Spring.Net\src\Spring\Spring.Web\Context\S upport\WebApplicationContext.cs:72

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.Reflection.RuntimeConstructorInfo.InternalI nvoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean isBinderDefault) +0
System.Reflection.RuntimeConstructorInfo.Invoke(Bi ndingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +413
System.Reflection.ConstructorInfo.Invoke(Object[] parameters) +14
Spring.Objects.ObjectUtils.InstantiateType(Constru ctorInfo constructor, Object[] arguments) in D:\Work\Spring.Net\src\Spring\Spring.Core\Objects\ ObjectUtils.cs:161

[FatalObjectException: Cannot instantiate Type [Spring.Context.Support.WebApplicationContext] using ctor [Void .ctor(System.String[])] : 'Exception has been thrown by the target of an invocation.']
Spring.Web.Support.PageHandlerFactory.GetHandler(H ttpContext context, String requestType, String url, String path) in D:\Work\Spring.Net\src\Spring\Spring.Web\Web\Suppo rt\PageHandlerFactory.cs:79
System.Web.HttpApplication.MapHttpHandler(HttpCont ext context, String requestType, String path, String pathTranslated, Boolean useAppConfig)
System.Web.MapHandlerExecutionStep.System.Web.Http Application+IExecutionStep.Execute()
System.Web.HttpApplication.ExecuteStep(IExecutionS tep step, Boolean& completedSynchronously)

Directories and control names are obfuscated but I've double checked them (the real ones). Seems that web type resolver doesn't take controls into account without the types. No infinite loops though ;)

-Marko

Aleks Seovic
09-02-2005, 04:17 PM
Sorry, you need to make definition abstract by adding abstract="true" attribute to your object element.

- Aleks

Ted Husted
09-02-2005, 08:44 PM
I've got global injecting working for my User Controls. (Though, I needed to put the elements in the "child" web.config, rather than the root, for some reason.) The parent option works great too.

I don't suppose that we can inject Event Handlers? Our controls all throw a common Event in the case of an error.

-Ted.

Aleks Seovic
09-02-2005, 08:50 PM
There should be a way to register even handlers as well, not sure what the state of eventing support in the core is.

Mark/Rick, could you guys reply to this?

- Aleks

Rick Evans
09-02-2005, 09:29 PM
Hiya Ted, Aleks

We do have support for declarative event wiring (i.e. injecting event handlers to events declaratively), and have done for almost a year now.

It is not documented because in a conversation we had about a year ago we couldn't think of a reason why we'd want to do this... I guess that question just got answered :D

Declarative event wiring needs oh, about 2 hours of work to be fully finished, polished, documented and production ready (it really is simply a matter of tweaking the XML configuration). I simply commented out the sections of the documentation that described the event wiring because well, like I say we didn't think anyone would find it useful.

Shall I put it back in? The functionality is already there, and if everyone agrees, I can have it in prior to the 1.0 final release going out. It is already extensively unit tested of course... the documentation just needs to be uncommented, slightly updated, and then we are cooking with gas 8)

On a related note, what about a previous forum question regarding AOP interception of event firing? Lets say I have an event... I want to intercept it being fired so that I can control the iteration through the event handlers and fire the event defensively (perhaps using the DefensiveEventHandler (http://www.springframework.net/doc/api/html/Spring.Util.DefensiveEventRaiser.html) from the core Spring.NET library). I want to apply this defensive event firing policy across a wide swath of the classes in my codebase (i.e. it is a cross cutting concern). Comments?

Ciao
Rick

lahma
09-03-2005, 02:54 PM
I got my control injection working with Aleks's examples. I hope you'll fix the web documentation to have correct configuration too (it still lacks the abstract="true").

Thanks again,

-Marko

Rick Evans
09-03-2005, 03:44 PM
Hiya

I have just updated the web documentation on the wiki (http://opensource2.atlassian.com/confluence/spring/display/NET/Dependency+Injection+for+ASP.Net+pages) to reflect the solution to the configuration issues raised in this forum thread.

Hopefully it won't be too long before everyone is reading bona-fide reference documentation as part of the Spring.NET 1.1 release :)

Ciao
Rick

Ted Husted
09-07-2005, 09:30 PM
Hiya Ted, Aleks
Shall I put it back in? The functionality is already there, and if everyone agrees, I can have it in prior to the 1.0 final release going out. It is already extensively unit tested of course... the documentation just needs to be uncommented, slightly updated, and then we are cooking with gas 8)


Yes, using controls as a building blocks for pages is working well for us. Once you start doing that, the next logical step is adding standard event handlers so that the controls can communicate back to the containing pages.

For example, if your master/child template has a centralized location for displaying error messages, the control can pass the message back to the page through an event handler.

Right now, we have to pass the controls through a standard registry event on the base page to set up the event handler, like this.

private void InitView(ViewControl c)
{
c.View_Error += new EventHandler(View_Error);
}

So, if a page uses five controls, we end up with a silly block like this in Page_load

InitView(routing_merger);
InitView(routing_editor);
InitView(facility_editor);
InitView(facility_lister);
InitView(facility_finder);

Right now, we're using the controls to program wizards, since we still haven't figured out how to use the Spring wizards :(

The containing page is nothing but controls, which it makes visible and invisible from the code-behind. The controls pass data back through event handlers, which the code-behind extracts and passes to other controls as needed.

-Ted.

Aleks Seovic
09-07-2005, 10:14 PM
Hi Ted,

Can you come up with an example of how you would like to wire those events in the config file. We had a number of discussions on the subject, but I'd like to see what someone who is actually building applications with Spring and needs this feature thinks.

Regards,

Aleks

Mark Pollack
10-28-2005, 01:30 AM
Hi,

One of the reasons we didn't doc the eventing stuff is that it did not work correctly for a singleton instance that wants to subscribe to a prototype event source. Also there were debates over if it is better for an object to say it is interested in publishing events to other objects or subscribing to event from other objects. I summarized an alternate approach that takes these issues into account in JIRA-SPRNET-41 (http://opensource2.atlassian.com/projects/spring/browse/SPRNET-41) - what do you think of that approach Ted?

Also, what about a more decoupled pub/sub mechanism that isn't even tied to directly identify the other object - like message oriented middlware messaging but w/out the external broker daemon process? I'd like the IEventRegistry to morph into that type of implementation.


Cheers,
Mark