I really dont understand what I'm doing wrong here - for some reason the IControllerFactory i register is not being used, and I end up with a System.ArgumentException:
Type 'Company.WebApi.Controllers.AController' does not have a default
constructor
at System.Linq.Expressions.Expression.New(Type type) at
System.Web.Http.Internal.TypeActivator.Create[TBase](Type
instanceType) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage
request, Type controllerType, Func`1& activator) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType)
In Global.asax.cs
protected void Application_Start()
{
//DI-setup
var container = new WindsorContainer().Install(new WebWindsorInstaller());
//set custom controller factory
var controllerFactory = container.Resolve<IControllerFactory>();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
//register cors
container.Resolve<ICorsConfig>().RegisterCors(GlobalConfiguration.Configuration);
//routes
RegisterRoutes(RouteTable.Routes);
}
My IControllerFactory is based on this article http://keyvan.io/custom-controller-factory-in-asp-net-mvc and is implemented as following
public class ControllerFactory : IControllerFactory
{
private readonly IWindsorContainer _container;
public ControllerFactory(IWindsorContainer container)
{
if(container == null)
throw new ArgumentNullException("container");
_container = container;
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
var componentName = GetComponentNameFromControllerName(controllerName);
return _container.Resolve<IController>(componentName);
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
throw new NotImplementedException();
}
public void ReleaseController(IController controller)
{
_container.Release(controller);
}
static string GetComponentNameFromControllerName(string controllerName)
{
var controllerNamespace = typeof (CrudController<>).Namespace;
return string.Format("{0}.{1}Controller", controllerNamespace, controllerName);
}
}
I've been staring at this for hours, and really can't see why this isn't working. When debugging the ControllerFactory is never hit in any method except the constructor. Anyone see whats wrong or missing here?
(Setting ASP.NET MVC ControllerFactory has no effect does not answer my question)
Edit 1 - Controller
public class AController : CrudController<AModel>
{
public AController(IAHandler aHandler) : base(aHandler)
{
...
}
[HttpGet]
public HttpResponseMessage GetByUser(string aId)
{
...
}
}
public abstract class CrudController<T> : ApiController
where T : IModel, new()
{
protected CrudController(ICrudHandler<T> handler)
{
...
}
[HttpGet]
public HttpResponseMessage Get(string id)
{
...
}
[HttpPost]
public HttpResponseMessage Post(T input)
{
...
}
[HttpPut]
public HttpResponseMessage Put(T input)
{
...
}
}
Edit 2 - Installer
public sealed class WebWindsorInstaller : IWindsorInstaller
{
private bool _installComplete;
public void Install(IWindsorContainer container, IConfigurationStore store)
{
if(_installComplete)
return;
//register self for reuse
container.Register(Component.For<IWindsorInstaller>().Instance(this));
//controller factory
container.Register(
Component.For<IControllerFactory>().ImplementedBy<ControllerFactory>().LifeStyle.Singleton);
//Handlers
container.Register(
Classes.FromAssemblyContaining<AHandler>().InSameNamespaceAs<AHandler>().WithService.
DefaultInterfaces());
//Models
container.Register(
Classes.FromAssemblyContaining<AModel>().InSameNamespaceAs<AModel>().WithService.Self());
//DI
container.Register(Component.For<IWindsorContainer>().Instance(container));
container.Register(Component.For<IDependencyResolver>().ImplementedBy<WindsorDependencyResolver>());
//Controllers
container.Register(Classes
.FromAssemblyContaining<AController>()
.BasedOn<IHttpController>()
.LifestyleScoped());
//repositories
container.Register(
Classes.FromAssemblyContaining<ARepository>().InSameNamespaceAs<ARepository>().WithService.
DefaultInterfaces());
//cors
container.Register(Component.For<ICorsConfig>().ImplementedBy<CorsConfig>());
_installComplete = true;
}
}
From the error messages in your question it looks like you actually want to register WebAPI controllers. It's important to note that these are not the same as MVC controllers, and do not use the same controller factory (which you are trying to use).
See Mark Seeman's post Dependency Injection in ASP.NET Web API with Castle Windsor for details of how to do this for Web API controllers.
Related
I have implemented a Windsor for my controller like described here
http://sitecore-estate.nl/wp/2014/12/sitecore-mvc-dependency-injection-using-castle-windsor/
and set up my WebApi like here https://kb.sitecore.net/en/Articles/2015/07/15/11/30/700677.aspx
for regular controller it is works good. But I wonder how to use it for ApiController. Next way is not working
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<IHttpController>().LifestyleTransient());
}
Yes answer for this question would be like use IHttpControllerActivator:
public class WindsorHttpControllerFactory : IHttpControllerActivator
{
private readonly IWindsorContainer _container;
public WindsorHttpControllerFactory(IWindsorContainer container)
{
_container = container;
}
public IHttpController Create(HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)_container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => _container.Release(controller)));
return controller;
}
class Release : IDisposable
{
readonly Action _release;
public Release(Action release)
{
_release = release;
}
public void Dispose()
{
_release();
}
}
}
public class WebApiInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Types.FromThisAssembly().BasedOn<IHttpController>().LifestyleTransient());
}
}
public class InitializeWindsorControllerFactory
{
public virtual void Process(PipelineArgs args)
{
SetupControllerFactory(args);
}
public virtual void SetupControllerFactory(PipelineArgs args)
{
IWindsorContainer container = new WindsorContainer().Install(FromAssembly.This());
IControllerFactory controllerFactory = new WindsorControllerFactory(container.Kernel);
SitecoreControllerFactory sitecoreControllerFactory = new SitecoreControllerFactory(controllerFactory);
System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(sitecoreControllerFactory);
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator),new WindsorHttpControllerFactory(container));
}
}
and config settings for
<pipelines>
<initialize>
<processor type="My.IoC.InitializeWindsorControllerFactory, My.IoC" patch:instead="*[type='Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc']"/>
</initialize>
</pipelines>
I have below code which will work without any issue
MAUserController.cs
public class MAUserController : ApiController
{
ILogService loggerService;
IMAUserService _service;
public MAUserController(ILogService loggerService, IMAUserService Service)
{
this.loggerService = loggerService;
this._service = Service;
}
}
DependencyInstaller.cs
public class DependencyInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<ILogService>().ImplementedBy<LogService>().LifeStyle.PerWebRequest,
Component.For<IDatabaseFactory>().ImplementedBy<DatabaseFactory>().LifeStyle.PerWebRequest,
Component.For<IUnitOfWork>().ImplementedBy<UnitOfWork>().LifeStyle.PerWebRequest,
AllTypes.FromThisAssembly().BasedOn<IHttpController>().LifestyleTransient(),
AllTypes.FromAssemblyNamed("ISOS.Health.Service").Where(type => type.Name.EndsWith("Service")).WithServiceAllInterfaces().LifestylePerWebRequest(),
AllTypes.FromAssemblyNamed("ISOS.Health.Repository").Where(type => type.Name.EndsWith("Repository")).WithServiceAllInterfaces().LifestylePerWebRequest()
);
}
}
If I am using normal Controller instead ApiController then it gives me an error
UserController.cs
public class UserController : Controller
{
ILogService loggerService;
IMAUserService _service;
public UserController(ILogService loggerService, IMAUserService Service)
{
this.loggerService = loggerService;
this._service = Service;
}
}
This will give an error:
No parameterless constructor defined for this object
I am using CastleDI Windsor for Dependency injection.
Do I need to do anything or register something?
FIRST APPROACH
Advice: Use with caution, because it may cause memory leaks for Castle Windsor.
You have to create a controller activator, which should implement the IControllerActivator interface, in order to use your DI container to create the controller instances:
public class MyWindsorControllerActivator : IControllerActivator
{
public MyWindsorControllerActivator(IWindsorContainer container)
{
_container = container;
}
private IWindsorContainer _container;
public IController Create(RequestContext requestContext, Type controllerType)
{
return _container.Resolve(controllerType) as IController;
}
}
Then, add this class to your DependencyInstaller:
public class DependencyInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
// Current code...
Component.For<IControllerActivator>()
.ImplementedBy<MyWindsorControllerActivator>()
.DependsOn(Dependency.OnValue("container", container))
.LifestyleSingleton();
);
}
}
Also, create your own dependency resolver based on the Windsor container:
public class MyWindsorDependencyResolver : IDependencyResolver
{
public MyWindsorDependencyResolver(IWindsorContainer container)
{
_container = container;
}
private IWindsorContainer _container;
public object GetService(Type serviceType)
{
return _container.Resolve(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _container.ResolveAll(serviceType).Cast<object>();
}
}
Then, finally, register your dependency resolver in the Application_Start method in Global.asax.cs:
DependencyResolver.SetResolver(new MyWindsorDependencyResolver(windsorContainer));
This way, when MVC requires the controller activator through it's dependency resolver, it will get ours, which will use our Windsor container to create the controllers with all it's dependencies.
In order to avoid memory leaks using IControllerActivator, the easiest solution will be to use lifestyles like per thread or per web request, rather than the default (Singleton), transient and pooled, for the registered components. Check this link for more info about how to avoid memory leaks using Castle Windsor Container.
SECOND APPROACH
However, as pointed out by #PhilDegenhardt, a much better and correct approach will be to implement a custom controller factory, in order to be able to release the controller component created by the Castle Windsor DI Container. Here you can find an example (see the section about Dependency Injection).
Taken from that example, the implementation could be:
Global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
{
private WindsorContainer _windsorContainer;
protected void Application_Start()
{
var _windsorContainer = new WindsorContainer();
_windsorContainer.Install(
new DependencyInstaller(),
// Other installers...
);
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(_windsorContainer.Kernel));
}
protected void Application_End()
{
if (_windsorContainer != null)
{
_windsorContainer.Dispose();
}
}
}
WindsorControllerFactory.cs:
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller); // The important part: release the component
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)_kernel.Resolve(controllerType);
}
}
Look at the following project link https://github.com/rarous/Castle.Windsor.Web.Mvc
Add this reference via NuGet to your MVC project, it will do the registering job for you.
Do not forget to catch your errors in global.asax.cs!
Registration :
container.Register(Component.For<IControllerFactory>().ImplementedBy<WindsorControllerFactory>());
Implementation of MVC controller factory :
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;
namespace Installer.Mvc
{
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
if (_kernel.GetHandler(controllerType) != null)
{
return (IController)_kernel.Resolve(controllerType);
}
return base.GetControllerInstance(requestContext, controllerType);
}
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller);
}
}
}
I have WebApi project with ODataController and I'm trying to inject some dependency into MyController. I was following this blogpost by Mark Seemann.
Consider code below.
Problem is, that when is MyController creating, I got exception inside WindsorCompositionRoot Create method on this line,
var controller = (IHttpController)this.container.Resolve(controllerType);
An exception of type 'Castle.MicroKernel.ComponentNotFoundException'
occurred in Castle.Windsor.dll but was not handled in user code
Additional information: No component for supporting the service
System.Web.OData.MetadataController was found
Any idea how to fix this?
Thank you.
My controller:
public class MyController : ODataController
{
private readonly DataLayer _db;
public PrepravyController(DataLayer db)
{
_db = db;
}
}
CompositonRoot:
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer container;
public WindsorCompositionRoot(IWindsorContainer container)
{
this.container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)this.container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
}
Global asax:
var container = new WindsorContainer();
container.Install(new RepositoriesInstaller());
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new WindsorCompositionRoot(container));
GlobalConfiguration.Configure(WebApiConfig.Register);
Make sure you're registering all your controllers with the container:
public class ControllerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestylePerWebRequest())
.Register(Classes.FromThisAssembly().BasedOn<ApiController>().LifestylePerWebRequest());
}
}
Windsor uses installers to encapsulate and partition registration logic. It also includes a helper called FromAssembly, so you don't need to manually instantiate all your installers:
_container = new WindsorContainer();
_container.Install(FromAssembly.This());
I'm having a difficult time wrapping my head around Mef and how imports and exports work. My project structure is as follows.
Projects:
MefMVPApp (Main MVC 4 app)
MefMVCFramework.Common(Interfaces shared between the projects)
MefMVCDemo.Plugins.OrderStatus (pluggable area.)
MefMVCDemo.Plugins.Data (Repository for OrderStatus)
OrderStatus.Models(domain models shared between the projects)
The goal of the main Mvc App will be to host plug-able areas via mef.
The OrderStatus Area has a controller called OrderStatusController and is decorated with the Export Attribute and a ImportingConstructor.
[Export(typeof(IController))]
[ExportMetadata("controllerName", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private readonly IRepository<OrderStatusApp.OrderStatusResponse>_repository ;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetAll();
return View();
}
}
IRepository is a class in the MefMVCFramework.Common assembly and will be used for generic CRUD operations.
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(int id);
void Add(T entity);
int SaveOrUpdate(T entity);
bool Delete(T entity);
bool Delete(int id);
}
The MefMVCDemo.Plugins.Data assembly contains a Class called OrderManagementRepository that inherents for the generic repository and is marked with an Export Attribute.
[Export(typeof(IRepository<OrderStatusApp.OrderStatusResponse>))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderManagementRepository : IRepository<OrderStatusApp.OrderStatusResponse>
{
private readonly JsonServiceClient _client;
public OrderManagementRepository()
{
_client = new JsonServiceClient("http://localhost:52266");
}
public IEnumerable<OrderStatusApp.OrderStatusResponse> GetAll()
{
throw new NotImplementedException("Can not get all");
}
public OrderStatusApp.OrderStatusResponse GetById(int id)
{
throw new NotImplementedException();
}
public void Add(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public int SaveOrUpdate(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(int id)
{
throw new NotImplementedException();
}
}
Using Mefx tool I am able to see my parts and there are no rejection.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /parts
MefMVCDemo.Plugins.Data.OrderManagementRepository
mefMVCDemo.Plugins.OrderStatus.Controllers.OrderStatusController
MefMVCDemo.Plugins.OrderStatus.Verbs.OrderStatusVerb
I can see my import.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /imports
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
Now when browse my main mvc site with the /orderstatus uri I get the following error:
No parameterless constructor defined for this object.
Adding a default constructor to the OrderStatusController that takes no overloads doesn't seem to work.
I guess the question is what am I doing wrong? Why does my interface in the constructor all way end up being null and why is there an mvc error about the "No parameterless constructor defined for this object".
The default controller factory in MVC tries to create the controller using a parameterless constructor. If you want to change this behavior, then you need to create your own custom controller factory.
Here is an example of a ControllerFactory using imports/exports on controllers
I'm using MEF for importing some parts to my app but my controllers are not imported/exported, so I created the following controller factory
public class ControllerFactory : IControllerFactory
{
private readonly CompositionContainer _container;
private IControllerFactory _innerFactory;
/// <summary>
/// Constructor used to create the factory
/// </summary>
/// <param name="container">MEF Container that will be used for importing</param>
public ControllerFactory(CompositionContainer container)
{
_container = container;
_innerFactory = new DefaultControllerFactory();
}
/// <summary>
/// Method used for create the controller based on the provided name. It calls the
/// constructor of the controller passing the MEF container
/// </summary>
/// <param name="requestContext">Context of the request</param>
/// <param name="controllerName">Name of the controller provided in the route</param>
/// <returns>The controller instance</returns>
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
Type controllerType = FindControllerByName(controllerName);
var args = new object[] { this._container };
var controller = (IController)Activator.CreateInstance(controllerType, args);
return controller;
}
/// <summary>
/// This methods looks into the current Assembly for the Controller type
/// </summary>
/// <param name="name">The controller name provided in the route</param>
/// <returns>The controller type</returns>
private static Type FindControllerByName(string name){
var a = Assembly.GetAssembly(typeof(ControllerFactory));
var types = a.GetTypes();
Type type = types.Where(t => t.Name == String.Format("{0}Controller", name)).FirstOrDefault();
return type;
}
public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return System.Web.SessionState.SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
}
}
Thank you pollirrata for pointing me in the right direction.
I had to change a few things to get this to work.
1.) I added an interface called INameMetadata to my MefMVCFramework.Common project.
public interface INameMetadata
{
string Name { get; }
}
2.) Modified My ExportMetadata Tag on my controller export to be Name, OrderStatus.
[Export(typeof(IController))]
[ExportMetadata("Name", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private IRepository<OrderStatusApp.OrderStatusResponse> _repository;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetById(47985);
return View(model);
}
}
3.) Created the MefControllerFactory (based off what pollirrata posted but modified to look for the Metadata)
public class MefControllerFactory : IControllerFactory
{
private string _pluginPath;
private readonly DirectoryCatalog _catalog;
private readonly CompositionContainer _container;
private DefaultControllerFactory _defaultControllerFactory;
public MefControllerFactory(string pluginPath)
{
_pluginPath = pluginPath;
_catalog = new DirectoryCatalog(pluginPath);
_container = new CompositionContainer(_catalog);
_defaultControllerFactory = new DefaultControllerFactory();
}
public MefControllerFactory(CompositionContainer compositionContainer)
{
_container = compositionContainer;
_defaultControllerFactory = new DefaultControllerFactory();
}
#region IControllerFactory Members
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
//IController controller = null;
var controller = _container.GetExports<IController,INameMetadata>()
.Where(e=>e.Metadata.Name.Equals(controllerName))
.Select(e=>e.Value).FirstOrDefault();
if (controller == null)
{
throw new HttpException(404, "Not found");
}
return controller;
}
public void ReleaseController(IController controller)
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
#endregion
public SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
}
4.) I created a Class called MefConfig in Main MVC app and moved it to the App_Start Dir.
public static class MefConfig
{
public static void RegisterMef()
{
//var builder = new RegistrationBuilder();
//builder.ForTypesDerivedFrom<IRepository<OrderStatusApp.OrderStatusResponse>>().Export<IRepository<IRepository<OrderStatusApp.OrderStatusResponse>>>();
var directoryCatalog = new DirectoryCatalog(HostingEnvironment.MapPath("~/bin"), "*.dll");
var container = new CompositionContainer(directoryCatalog, true);
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
//Working
//ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(HostingEnvironment.MapPath("~/bin")));
// Install MEF dependency resolver for MVC
var resolver = new MefDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
// Install MEF dependency resolver for Web API
GlobalConfiguration.Configuration.DependencyResolver = resolver;
var d = container.GetExportedValues<IRepository<OrderStatusApp.OrderStatusResponse>>();
//Mefx.
try
{
//var ci = new CompositionInfo(aggregateCatalog, container);
var ci = new CompositionInfo(directoryCatalog, container);
var partDef = ci.GetPartDefinitionInfo(typeof(IRepository<OrderStatusApp.OrderStatusResponse>));
//var possibleCauses = partDef.FindPossibleRootCauses();
var stringWriter = new StringWriter();
CompositionInfoTextFormatter.Write(ci, stringWriter);
var compStatString = stringWriter.ToString();
}
catch
{
}
MvcApplication.ActionVerbs = container.GetExports<IActionVerb, IActionVerbMetadata>();
}
}
5.) Load the Mefconfig from the global.asax.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
//Register Mef
MefConfig.RegisterMef();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
I'm going over Sanderson's Pro ASP.NET MVC Framework and in Chapter 4 he discusses Creating a Custom Controller Factory and it seems that the original method, AddComponentLifeStyle or AddComponentWithLifeStyle, used to register controllers is deprecated now:
public class WindsorControllerFactory : DefaultControllerFactory
{
IWindsorContainer container;
public WindsorControllerFactory()
{
container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));
// register all the controller types as transient
var controllerTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
where typeof(IController).IsAssignableFrom(t)
select t;
//[Obsolete("Use Register(Component.For<I>().ImplementedBy<T>().Named(key).Lifestyle.Is(lifestyle)) instead.")]
//IWindsorContainer AddComponentLifeStyle<I, T>(string key, LifestyleType lifestyle) where T : class;
foreach (Type t in controllerTypes)
{
container.Register(Component.For<IController>().ImplementedBy<???>().Named(t.FullName).LifeStyle.Is(LifestyleType.Transient));
}
}
// Constructs the controller instance needed to service each request
protected override IController GetControllerInstance(Type controllerType)
{
return (IController)container.Resolve(controllerType);
}
}
The new suggestion is to use Register(Component.For<I>().ImplementedBy<T>().Named(key).Lifestyle.Is(lifestyle)), but I can't figure out how to present the implementing controller type in the ImplementedBy<???>() method. I tried ImplementedBy<t>() and ImplementedBy<typeof(t)>(), but I can't find the appropriate way to pass in the implementing type. Any ideas?
I'm doing this using the ControllerBuilder.SetControllerFactory and the code you can find in the open source project MvcContrib:
Global.asax.cs
protected void Application_Start()
{
...
IWindsorContainer windsorContainer = new WindsorContainer();
windsorContainer.RegisterControllers(Assembly.GetExecutingAssembly());
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(windsorContainer));
...
}
WindsorControllerFactory
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IWindsorContainer _container;
public WindsorControllerFactory(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException();
}
_container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException();
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException();
}
try
{
return (IController)_container.Resolve(controllerType);
}
catch (Exception ex)
{
throw new InvalidOperationException();
}
}
public override void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
_container.Release(controller);
}
}
WindsorExtensions (see MvcContrib)
public static class WindsorExtensions
{
public static IWindsorContainer RegisterController<T>(this IWindsorContainer container) where T : IController
{
container.RegisterControllers(typeof(T));
return container;
}
public static IWindsorContainer RegisterControllers(this IWindsorContainer container, params Type[] controllerTypes)
{
foreach (Type type in controllerTypes)
{
if (ControllerExtensions.IsController(type))
{
container.Register(Component.For(type).Named(type.FullName).LifeStyle.Is(LifestyleType.Transient));
}
}
return container;
}
public static IWindsorContainer RegisterControllers(this IWindsorContainer container, params Assembly[] assemblies)
{
foreach (Assembly assembly in assemblies)
{
container.RegisterControllers(assembly.GetExportedTypes());
}
return container;
}
}
ControllerExtensions (see MvcContrib)
public static class ControllerExtensions
{
public static bool IsController(Type type)
{
return type != null
&& type.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)
&& !type.IsAbstract
&& typeof(IController).IsAssignableFrom(type);
}
}
You may also want to consider using the new installer option in the latest Windsor build. There is more documentation on Windsor's tutorial: http://stw.castleproject.org/Windsor.Windsor-tutorial-part-three-writing-your-first-installer.ashx
There's a tutorial (in the works but 9 parts are already out) that discusses usage of Windsor in ASP.NET MVC here.
That's the most up to date and covering most of the usual usage resource on the topic as far as I'm aware.
#Lirik, as an addition: drop your own custom IControllerFactory out if you use MVC3. Just register controllers with Windsor and implement IDependencyResolver with Windsor container inside.
Set your IDependencyResolver as MVC DependencyResolver and DefaultControllerFactory will automatically wire up controllers registered in container (via DependencyResolver).
something like:
public void Register(IWindsorContainer container)
{
Assembly.GetAssembly(typeof(ControllersRegistrarMarker)).GetExportedTypes()
.Where(IsController)
.Each(type => container.AddComponentLifeStyle(
type.Name.ToLower(),
type,
LifestyleType.Transient));
}
ControllersRegistrarMarker is just an empty class in your Controllers assembly