82553

ASP.NET MVC 4 RC with Castle Windsor

I was taking a look at ASP.NET MVC 4 RC and cannot find DefaultHttpControllerFactory or even IHttpControllerFactory to allow my IoC container of choice (Castle Windsor) to hook into the framework for Web Api controllers. I ended up using IDependencyResolver which makes releasing components a bit trickier, but ended up with the following. Is it going to work / not memory leak until IDependencyResolver has a Release method? Global.asax ends up as:

public class WebApiApplication : System.Web.HttpApplication { private IWindsorContainer container; protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); Windsor(); } protected void Application_End() { container.Dispose(); } private void Windsor() { container = new WindsorContainer().Install(FromAssembly.This()); // mvc: var mvcControllerFactory = new WindsorControllerFactory(container.Kernel); ControllerBuilder.Current.SetControllerFactory(mvcControllerFactory); // web api: var httpDependencyResolver = new WindsorHttpDependencyResolver(container.Kernel); GlobalConfiguration.Configuration.DependencyResolver = httpDependencyResolver; } }

WindsorControllerFactory is extension of DefaultControllerFactory for Mvc Controllers and there is a Windsor installer for them. WindsorHttpDependencyResolver ends up as:

public class WindsorHttpDependencyResolver : System.Web.Http.Dependencies.IDependencyResolver { private readonly IKernel kernel; public WindsorHttpDependencyResolver(IKernel kernel) { this.kernel = kernel; } public IDependencyScope BeginScope() { return kernel.Resolve<IDependencyScope>(); // instances released suitably (at end of web request) } public object GetService(Type serviceType) { // for ModelMetadataProvider and other MVC related types that may have been added to the container // check the lifecycle of these registrations return kernel.HasComponent(serviceType) ? kernel.Resolve(serviceType) : null; } public IEnumerable<object> GetServices(Type serviceType) { return kernel.HasComponent(serviceType) ? kernel.ResolveAll(serviceType) as IEnumerable<object> : Enumerable.Empty<object>(); } public void Dispose() { // Nothing created so nothing to dispose - kernel will take care of its own } }

Theoretically, this means Windsor will now provide the Api Controllers, once they are installed:

public class ApiControllersInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.AddFacility<TypedFactoryFacility>(); container.Register(Component.For<ITypedFactoryComponentSelector>().ImplementedBy<WebApiTypedFactoryComponentSelector>()); container.Register(Component.For<IDependencyScope>().AsFactory(tfc => tfc.SelectedWith<WebApiTypedFactoryComponentSelector>()).LifestylePerWebRequest()); container.Register(Classes.FromAssemblyContaining<ValuesController>().BasedOn<IHttpController>().LifestyleTransient()); } }

I'm using the typed factory facility to implement IDependencyScope for me, which means when the framework disposes it at the end of a request, it will release the controller and its dependencies implicitly. By using a Per Web Request life cycle, Windsor will release the factory itself too. That just leaves the custom typed factory component selector as GetService(s) will not find anything in the container:

public class WebApiTypedFactoryComponentSelector : DefaultTypedFactoryComponentSelector { protected override string GetComponentName(System.Reflection.MethodInfo method, object[] arguments) { if (method.Name == "GetService" || method.Name == "GetServices") { return (arguments[0] as Type).FullName; } return base.GetComponentName(method, arguments); } protected override Type GetComponentType(System.Reflection.MethodInfo method, object[] arguments) { if (method.Name == "GetService" || method.Name == "GetServices") { return arguments[0] as Type; } return base.GetComponentType(method, arguments); } }

Hope this is useful.

Answer1:

I ended up using code from this blog post (further enhanced by this other one by Mark Seemann), which creates a scope per request and takes care of releasing the created objects. This is a slightly different approach to the one you've taken.

Recommend

  • Unable to perform dependency injection in MVC 5 Web API project using Castle Windsor
  • asp.net mvc test project cannot find windsor file
  • Wcf Castle Windsor
  • How can I configure a Factory with the possible providers?
  • Android: how to rotate image using Fresco
  • selenium-server: Unexpected status SERVICE_PAUSED
  • C# TPL calling tasks in a parallel manner and asynchronously creating new files
  • how to end the nested loop
  • Optimising asyncronus HttpClient requests
  • Dispose not working as expected in DirectorySearcher
  • Mod rewrite redirection to another domain if file not exist
  • How to bind comma separated list of values to List
  • ASPNetCore MVC Routing Let Server Handle Specific Route
  • Mixing WebForms and MVC: What should I do with the MasterPage?
  • OSX - always hide certain files
  • Bigquery event streaming and table creation
  • Compiling dlib on OS X
  • msbuild create itemgroup from property group
  • Kendo barchart category labels left and right based on value
  • Make new pandas columns based on pipe-delimited column with possible repeats
  • How to get current document uri in XSLT?
  • How to generate and display a QR Code in ionic 2
  • WPF ICommand CanExecute(): RaiseCanExecuteChanged() or automatic handling via DispatchTimer?
  • ViewController With Transparent Background Entering Current ViewController With Push Transition
  • Remove final comma from string in vb.net
  • onBackPressed() not being executed
  • Webgrid not refreshing after delete MVC
  • MVC3 Razor - ListBox pre-select not working
  • Make VS2015 use angular-cli ng at build time in a .NET project
  • Refering to the class itself from within a class mehod in Objective C
  • Alert pop up with LWUIT
  • To display the title for the current loaction in map in iphone
  • Invalid access key error using credentials redeemed from an amazon open id token
  • PHP: When would you need the self:: keyword?
  • Acquiring multiple attributes from .xml file in c#
  • using HTMLImports.whenReady not working in chrome
  • Authorize attributes not working in MVC 4
  • EntityFramework adding new object to nested object collection
  • reshape alternating columns in less time and using less memory
  • How can I use threading to 'tick' a timer to be accessed by other threads?