Castle Windsor ASP.NET MVC 4 and Web API same application. Only if MVC controllers have no dependencies?
Asked Answered
A

3

5

This article is a good starting point for doing Web API with Castle Windsor, but what if I create a simple MVC controller? It only works if there is no dependency into the inject.

Adding this one, i.e.:

public class HomeController : Controller
{
    private readonly IValuesRepository repository;

    public HomeController(IValuesRepository repository)
    {
        this.repository = repository;
    }

    public ActionResult Index()
    {
        return View();
    }
}

Causes the following error :

parameterless constructor defined for this object

Is there a way to have MVC and Web API logic in a same application using Castle Windsor?

After setting DependencyResolver.SetResolver(...) in application_start, I didn't notice any improvement in my application.

As you can see.

The type WebApiScopedLifetimeDependencyResolverSample.Windsor.WindsorDependencyResolver does not appear to implement

Implementing the service locator :

internal sealed class WindsorDependencyResolver
    : ServiceLocatorImplBase, IDependencyResolver
{
    private readonly IWindsorContainer container;

    public WindsorDependencyResolver(
        IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        this.container = container;
    }

    public object GetService(Type t)
    {
        return this.container.Kernel.HasComponent(t)
             ? this.container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return this.container.ResolveAll(t)
            .Cast<object>().ToArray();
    }

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(this.container);
    }

    public void Dispose()
    {
    }

    protected override object DoGetInstance(
        Type serviceType, string key)
    {
        if (key != null)
            return container.Resolve(key, serviceType);
        return container.Resolve(serviceType);
    }

    protected override IEnumerable<object> DoGetAllInstances(
        Type serviceType)
    {
        return (object[])container.ResolveAll(serviceType);
    }

took me back to the starting point.

Finally solved.

Here's the solution for someone else


In application_start...

//mvc
DependencyResolver.SetResolver(
    new WindsorMvcDependencyResolver(container));

// web api:
var httpDependencyResolver = 
    new WindsorHttpDependencyResolver(container);

GlobalConfiguration.Configuration.DependencyResolver =
    httpDependencyResolver;

internal class WindsorMvcDependencyResolver
    : WindsorDependencyScope, IDependencyResolver
{
    private readonly IWindsorContainer container;

    public WindsorMvcDependencyResolver(
        IWindsorContainer container) : base(container)
    {
        this.container = container;
    }
}


internal sealed class WindsorHttpDependencyResolver
    : IDependencyResolver
{
    private readonly IWindsorContainer container;

    public WindsorHttpDependencyResolver(
        IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        this.container = container;
    }

    public object GetService(Type t)
    {
        return this.container.Kernel.HasComponent(t)
             ? this.container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return this.container.ResolveAll(t)
            .Cast<object>().ToArray();
    }

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(this.container);
    }

    public void Dispose()
    {
    }
}

internal class WindsorDependencyScope : IDependencyScope
{
    private readonly IWindsorContainer container;
    private readonly IDisposable scope;

    public WindsorDependencyScope(
        IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        this.container = container;
        this.scope = container.BeginScope();
    }

    public object GetService(Type t)
    {
        return this.container.Kernel.HasComponent(t) 
            ? this.container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return this.container.ResolveAll(t)
            .Cast<object>().ToArray();
    }

    public void Dispose()
    {
        this.scope.Dispose();
    }
}

And the registration for both mvc and web api controller

container.Register(Classes
    .FromAssemblyContaining<HomeController>()
    .BasedOn<Controller>()
    .LifestylePerWebRequest());


container.Register(Classes
    .FromAssemblyContaining<ValuesController>()
    .BasedOn<IHttpController>()
    .LifestyleScoped());
Aeolus answered 28/9, 2012 at 15:6 Comment(1)
There are 2 different dependency resolvers (in different namespaces) for the Web API and MVC.Rancell
C
6

Microsoft seems to promote putting Web API into the same project as an MVC application (caused by the template they use). However, I wouldn't mix a Web API and an MVC application into the same project/website. It is very likely that the requirements and (DI) configurations need to be different, which will make everything complex trying to solve this using one single container configuration.

So although it is completely possible to have MVC and Web API running in the same application, and it is possible with a single container, my advice is: don't do this. Not only from a technical perspective, but also from a functional. A Web API is a Web Service. You also wouldn't mix MVC with WCF in the same project.

But if you really want to, you'll just have to register both the System.Web.Mvc.DependencyResolver.SetResolver (for MVC) and System.Web.Http.HttpConfiguration.DependencyResolver (for Web API).

Constabulary answered 29/9, 2012 at 9:39 Comment(5)
I generally agree with your considerations, but the simple nature of the application -a configuration editor for intranet client applications that interacts through rest services- gives me the opportunity to experiment with this solution.Aeolus
Just a note: registering both the resolvers cause the mvc resolver to activate also for http routes.Aeolus
There may be reasons to mix MVC and Web API applications. In order to support IE9 on a site with both MVC and lots of SPA functionality backed by a Web API solution, we had to co-host both applications, because IE9 doesn't support CORS. We still built the MVC and Web API applications as two completely independent libraries, and then host both in the same web site.Incrustation
@MarkSeemann: I think my argument still stands. The only thing you seem to do is to deploy those applications together, but architecturally, you separated them and they probably each have their own composition root.Constabulary
@Constabulary Yes, that is indeed the case... I also did upvote your answer :)Incrustation
W
0

WindsorDependencyScope will work if you are implementing the IDependencyResolver interface and wire it up with the framework int the Application_Start: DependencyResolver.SetResolver()

Willin answered 29/9, 2012 at 4:55 Comment(0)
A
0

To make it works on Owin Startup I override the DependencyResolver of the HttpConfiguration

[assembly: OwinStartup(typeof(YourCompany.API.Startup))]
namespace YourCompany.API
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            var dependencyResolver = new WindsorHttpDependencyResolver(container);
            config.DependencyResolver = dependencyResolver;
            // ....... 
            // ....... 
        }
    }
}
Altamirano answered 5/11, 2015 at 15:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.