views:

323

answers:

3

I am trying to create an ASP.NET MVC application, using Spring.NET to inject dependencies. The application has three tiers: Controller, Service, and Data.

I have defined the objects in the file "~\Resources\objects.xml".

My first object, UserAccountController, requires the injecion of two Service-tier classes: UserAccountService and DepartmentService. So, the definition in objects.xml looks like this:

<object id="UserAccountController" type="App.Controllers.UserAccountController, App">
    <constructor-arg index="0" ref="DepartmentService" />
    <constructor-arg index="1" ref="UserAccountService" />
</object>

<object id="UserAccountService" type="App.Service.UserAccountService, App">
    <property name="UserAccountDao" ref="UserAccountDao" />
</object>

<object id="UserAccountDao" type="App.Data.UserAccountDao, App" />

<object id="DepartmentService" type="App.Service.DepartmentService, App">
    <property name="DepartmentDao" ref="DepartmentDao" />
</object>

<object id="DepartmentDao" type="App.Data.DepartmentDao" />

Webconfig contains this:

<sectionGroup name="spring">
        <section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
    </sectionGroup>
</configSections>
<spring>
    <context>
        <resource uri="~/Resources/objects.xml" />
    </context>
</spring>

I would prefer to use Property injection rather than constructor, but currently neither method is working.

+1  A: 

in your bootstrapclass you have to load the spring container

ContextRegistry.getContext();

by the way you need to specify the assembly name for DepartmentDao

<object id="DepartmentDao" type="App.Data.DepartmentDao, App" />
Fabiano
I actually have a SpringApplicationController class and a Spring ControllerFactory class, and reference them in Global.asax.cs:ControllerBuilder.Current.SetControllerFactory(typeof(App.Util.SpringControllerFactory));Do I still need to get the context if I'm using this method?
Jason
honestly, I don't know. But obviously not. Best practice for Inversion of Control is to load the container at the start of the application and have no other references to the container. If the configuration is done well a call to ContextRegistry.getContext(); should handle everything
Fabiano
Okay, I dumped the fancy ControllerFactory and ApplicationContext implementations in favor of a simple call to ContextRegistry.getContext(), and switched back to the regular ContextHandler in web.config. Still no luck on the injection.
Jason
how do you inject/reference UserAccountController?
Fabiano
UserAccountController is called from a form action. In this case, I have a Partial View named ViewCreate.ascx, which is called via a form action (from a form button titled "Create Account"). Click the button, and the partial view is called. Unfortunately, it dies due to the lack of the dependency being injected.
Jason
UserAccountController needs to be injected to the action too. or retrieved by the antipattern IApplicatonContext.GetObject("UserAccountController"). or retrieved via a static factory in which UserAccountController was injected
Fabiano
I don't know if I understand (please bear with me, I'm trying...). The UserAccountController is defined in objects.xml, but since I'm using MVC with a Partial View, there is no code-behind to inject the Controller into. The controller is the top tier, nothing higher to inject it into.I attempted to create a default constructor in UserAccountController to create an XmlApplicationContext, then get the objects from that, but got a Stackoverflow exception (probably circular references).
Jason
ok, I don't understand a lot of asp.net mvc. your first approach might have been correct. maybe this can help http://weblogs.asp.net/fredriknormen/archive/2007/11/17/asp-net-mvc-framework-create-your-own-icontrollerfactory-and-use-spring-net.aspx . and yes if you create a applicationContext in the constructor there is a circular reference
Fabiano
A: 

Further information: I also have classes for SpringApplicationController and SpringControllerFactory:

SpringApplicationController.cs:

public static class SpringApplicationContext
{
    private static IApplicationContext Context { get; set; }

    /// <summary> 
    /// Returns a boolean value if the current application context contains an named object. 
    /// </summary> 
    /// <param name="objectName">Accepts the name of the object to check.</param> 
    public static bool Contains(string objectName)
    {
        SpringApplicationContext.EnsureContext();
        return SpringApplicationContext.Context.ContainsObject(objectName);
    }

    /// <summary> 
    /// Return a instance of an object in the context by the specified name. 
    /// </summary> 
    /// <param name="objectName">Accepts a string object name.</param> 
    public static object Resolve(string objectName)
    {
        SpringApplicationContext.EnsureContext();
        return SpringApplicationContext.Context.GetObject(objectName);
    }

    /// <summary> 
    /// Return a instance of an object in the context by the specified name and type. 
    /// </summary> 
    /// <typeparam name="T">Accepts the type of the object to resolve.</typeparam> 
    /// <param name="objectName">Accepts a string object name.</param> 
    public static T Resolve<T>(string objectName)
    {
        return (T)SpringApplicationContext.Resolve(objectName);
    }

    private static void EnsureContext()
    {
        if (SpringApplicationContext.Context == null)
        {
            SpringApplicationContext.Context = ContextRegistry.GetContext();
        }
    }
} 

SpringControllerFactory.cs:

public class SpringControllerFactory : DefaultControllerFactory
{
    public IController CreateController(RequestContext context, Type controllerType)
    {
        IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("Resource\\objects.xml"));
        IObjectFactory factory = new XmlObjectFactory(input);

        return (IController) factory.GetObject(controllerType.Name);
    }

    public IController CreateController(RequestContext context, string controllerName)
    {
        IController controller = null;
        string controllerClassName = string.Format("{0}Controller", controllerName);

        if (SpringApplicationContext.Contains(controllerClassName))
        {
            controller = SpringApplicationContext.Resolve<IController>(controllerClassName);
            this.RequestContext = context;
        }
        else
        {
            controller = base.CreateController(context, controllerName);
        }
        return controller; 
    }

    public override void ReleaseController(IController controller)
    {
        IDisposable disposable = controller as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    } 
}

I reference this in my Global.asax as follows:

    protected void Application_Start()
    {

        ControllerBuilder.Current.SetControllerFactory(typeof(App.Util.SpringControllerFactory));


        RegisterRoutes(RouteTable.Routes);
    }
Jason
where is IApplicationContext Context set in SpringApplicationContext?
Fabiano
+1  A: 

Well, it turned out to be that ASP.NET MVC and Spring.NET just don't get along...

However, the MvcContrib package (actually, the Extras package) seems to have solved the issue. The package had a Spring Controller factory implementation that worked, and everything was happy.

(Kind of reminds me of trying to make Struts 1.X and Spring work on the Java side...)

Jason