So I went through the asp.net mvc tutorial for castle windsor, and my registrations look like:
private static IWindsorContainer _container = new WindsorContainer();
private static void BootstrapContainer()
{
_container = new WindsorContainer()
.Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
protected void Application_End()
{
_container.Dispose();
}
So in my HomeController I have this:
public class HomeController : Controller
{
private IUserService _userService;
public HomeController(IUserService userService)
{
this._userService = userService;
}
}
How would I go about wiring this controller up to setup the IUserService?
Update
In case in matters how I need to wire things up, my vs.net projects are:
web, interfaces, entities, data (nhibernate), services
The implementation of WindsorControllerFactory should look like this from the doco http://docs.castleproject.org/Windsor.Windsor-tutorial-part-two-plugging-Windsor-in.ashx?HL=ikernel.
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
// Throw exception. Can't resolve null type.
}
return (IController)kernel.Resolve(controllerType);
}
}
UPDATED
Each interface that needs to be resolved by dependancy injection need to be registered.
This can be done by calling the .Register method on the container.
container.Register(Component.For<IUserService>().ImplementedBy<UserService>().LifeStyle.Transient);
More info here: http://docs.castleproject.org/Windsor.Registering-components-one-by-one.ashx
Related
I have ASP.NET MVC application with integrated Castle Windsor container. When I added ApiController, and tried to do POST, I got an error message: "Make Sure That the controller has a parameterless public constructor." Thanks for any advice.
Castle Windsor integration:
public class CastleControllerFactory : DefaultControllerFactory
{
public IWindsorContainer Container { get; protected set; }
public CastleControllerFactory(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentException("container");
}
this.Container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return null;
}
return Container.Resolve(controllerType) as IController;
}
public override void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
Container.Release(controller);
}
}
public class DefaultCastleInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<TypedFactoryFacility>();
//Functions
//container.Register(Component.For<IGetIPAddressesFromRange>().ImplementedBy<GetIPAddressesFromRange>().LifestylePerWebRequest());
container.Register(Component.For<IUnitOfWork>().ImplementedBy<UnitOfWork>().LifestylePerWebRequest());
var contollers = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.BaseType == typeof(Controller)).ToList();
foreach (var controller in contollers)
{
container.Register(Component.For(controller).LifestyleTransient());
}
var apiContollers = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.BaseType == typeof(ApiController)).ToList();
foreach (var apiController in apiContollers)
{
container.Register(Component.For(apiController).LifestyleTransient());
}
}
}
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Integrace CastleWindsor
var container = new WindsorContainer();
container.Install(new DefaultCastleInstaller());
var castleControllerFactory = new CastleControllerFactory(container);
ControllerBuilder.Current.SetControllerFactory(castleControllerFactory);
}
}
ApiController:
public class UserVerificationController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
/// <summary>
/// Konstruktor
/// </summary>
/// <param name="unitOfWork"></param>
public UserVerificationController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
The problem is that because MVC and Web API controllers are separate entities, you need Windsor configuration for both. You've got Windsor configured to create your MVC controllers. You just need the same for your Web API controllers.
Here's a Web API controller activator, mostly lifted from this blog post.
public class WindsorWebApiControllerActivator : IHttpControllerActivator
{
private readonly IWindsorContainer _container;
public WindsorWebApiControllerActivator(IWindsorContainer container)
{
_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)
{
_release = release;
}
public void Dispose()
{
_release();
}
}
}
And you need to register all of the controller types with the container. Here's an IWindsorInstaller that registers all MVC and Web API controllers:
public class ControllersInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Classes.
FromThisAssembly().
BasedOn<IController>(). //MVC
If(c => c.Name.EndsWith("Controller")).
LifestyleTransient());
container.Register(
Classes.
FromThisAssembly().
BasedOn<IHttpController>(). //Web API
If(c => c.Name.EndsWith("Controller")).
LifestyleTransient());
}
}
Then at startup replace the default Web API controller activator with yours:
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
new WindsorWebApiControllerActivator(container));
and be sure to call the installer:
container.Install(new ControllersInstaller());
tl;dr: I'm creating Type (by reflection) that is extending ApiController. How can I dynamically register it (it can be registered at startup; no need to do this at runtime).
Long story:
So in my application I have multiple interfaces, i.e.:
interface IMyInterface
{
MyResponse Hello(MyRequest request);
}
The thing that I want to achive is for each interface create controller that should look like this:
public class IMyInterfaceController : ApiController
{
public IMyInterface MyInterface { get; set; }
public MyResponse Hello([FromBody] MyRequest request)
{
return MyInterface.Hello(request);
}
}
Generating this controller is already done using heavy C# reflection. The thing is that I want to do right now is to register this controller under /api/{controller}/{action}.
In Global.asax right now I got this:
public class MvcApplication : System.Web.HttpApplication
{
private readonly InterfaceReader _reader = new InterfaceReader(); // this class is doing all staff with reflection to create controller class
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
var controller = _reader.CreateController(new MyImplementation()); // MuImplementation implements IMyInterface
}
}
Solution with IHttpControllerFactory
I guess that what you need is a controller factory:
public class MyHttpControllerFactory : IHttpControllerFactory
{
private readonly InterfaceReader _reader;
private readonly HttpConfiguration _configuration;
public MyHttpControllerFactory(InterfaceReader reader, HttpConfiguration configuration)
{
_reader = reader;
_configuration = configuration;
}
public IHttpController CreateController(HttpControllerContext controllerContext, string controllerName)
{
if (controllerName == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", controllerContext.Request.RequestUri.AbsolutePath));
}
// Change the line below to whatever suits your needs.
var controller = _reader.CreateController(new MyImplementation());
controllerContext.Controller = controller;
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(configuration, controllerName, controller.GetType());
return controllerContext.Controller;
}
public void ReleaseController(IHttpController controller)
{
// You may want to be able to release the controller as well.
}
}
Then in the Global.asax you need to register the custom controller factory:
public class MvcApplication : System.Web.HttpApplication
{
private readonly InterfaceReader _reader = new InterfaceReader(); // this class is doing all staff with reflection to create controller class
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configuration.ServiceResolver.SetService(typeof(IHttpControllerFactory), new MyHttpControllerFactory(_reader, GlobalConfiguration.Configuration));
}
}
Solution with IHttpControllerActivator
If you use Web Api 2 then the solution is to use either IDependencyResolver or IHttpControllerActivator instead of the factory. I guess that the IHttpControllerActivator is better option in your case.
public class MyServiceActivator : IHttpControllerActivator
{
private readonly InterfaceReader _reader;
private readonly HttpConfiguration _configuration;
public MyServiceActivator(InterfaceReader reader, HttpConfiguration configuration)
{
_reader = reader;
_configuration = configuration;
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
// Change the line below to whatever suits your needs.
var controller = _reader.CreateController(new MyImplementation());
return controller;
}
}
Then in the Global.asax you need to register the custom activator:
public class MvcApplication : System.Web.HttpApplication
{
// this class is doing all staff with reflection to create controller class
private readonly InterfaceReader _reader = new InterfaceReader();
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Services.Replace(typeof(IHttpControllerActivator), new MyServiceActivator(_reader, config));
}
}
I hope this helps.
You need to create your own class that implements IHttpControllerActivator
Here's an example that I use for DI with Windsor
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer _container;
public WindsorCompositionRoot(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;
}
private class Release : IDisposable
{
private readonly Action _release;
public Release(Action release)
{
_release = release;
}
public void Dispose()
{
_release();
}
}
}
Then you need to wire it up in the Global.asax in Application_Start()
Container = new WindsorContainer().Install(FromAssembly.This());
GlobalConfiguration.Configuration.Services.Replace(
typeof (IHttpControllerActivator),
new WindsorCompositionRoot(Container));
Installing the controllers is done in a Windsor Installer class
public class ControllersInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IHttpController>()
.LifestyleTransient());
}
}
How do I connect the various pieces of my Web API Castle Windsor DI code so that the Controller's routing selects the correct interface implementation?
Note: After several false starts/dead ends and partial victories (here and here and here), I am going to bountify this ASAP for the maximum 500 points. But I'm only going to award a really good answer - IOW, one that is clear enough that I can understand it and "plug it in" to my project so that I can hook a given concrete class to a particular Controller.
Here goes nothing: I have a Web API ("MVC") project. Really, though, this server project has no "V" (View), so maybe a better acronym would be MRC (Model/Repository/Controller).
At any rate, I'm trying to add DI to it using Castle Windsor.
I grok, and dig, the concept of swapping out concrete classes via constructor interface args. Just how to implement this functionality, though,
has been a beast I've been wrestling with, and I'm quite bruised and bloody at present, with mussed-up hair and mud-encrusted nostrils to boot.
I have, I think, most of the code I need - to start with, anyway. With DI in mind, I've now got a "DIPlumbing" folder, and a "DIInstallers" folder.
The "DIPlumbing" folder contains two classes: WindsorCompositionRoot.cs, and WindsorControllerFactory.cs.
The "DIInstallers" folder has, for now, three files, namely ISomethingProvider.cs, SomethingProvider.cs, and SomethingProviderInstaller.cs
SomethingProviderInstaller seems to be key in connecting the interfaces/classes in DIInstallers to the stuff in the DIPlumbing folder.
(I have also modified Global.asax.cs to replace the default controller routing business with the Castle Windsor replacement).
But I'm confused as to what classes I should be placing in the DIInstallers folder. Are these supposed to take the place of my Repositories (which likewise have an interface and a concrete class that implements that interface for each model)? IOW, should I basically move my Repository code into the DIInstallers folder - and then get rid of the IRepository and Repository units?
This, of course, would cause necessary changes to be made in the Controller classes, which as of now reference Repository classes.
Or do the Repositories and DIInstallers classes coexist? If so, what is the connection/affiliation between the Controllers, Installers, and Repositories?
It seems the more I read up on DI and Castle Windsor, the more confused I get. I don't know if I'm too dense for it, or if I'm trying to make it harder than it is, or if conflicting styles of using/presenting it are the problem. The bottom line is: I'm stuck in quicksand and need Johnny Quest to extend a sturdy bamboo rod.
The best answer of all, perhaps, and probably too much to ask for, would be a visual representation of how all these components - Controllers, Models, Repositories, Installers, Global.asax.cs, composition roots, factories, providers, etc., relate to each other.
For purposes of "full disclosure," I will add what I hope are the key elements of my code below to show what I've got and how it (hopefully) connects to each other.
Composition Root:
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();
}
}
}
Controller Factory:
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
//According to my understanding of http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx, I might need this:
kernel.AddFacility<TypedFactoryFacility>();
}
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);
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
// Note: The "Something" below will hopefully eventually be "Departments" and then other classes now represented in Models and their corresponding Repositories and Controllers
ISomethingProvider:
public interface ISomethingProvider
{
// These are placeholder methods; I don't know which I will need yet...
//bool Authenticate(string username, string password, bool createPersistentCookie);
//void SignOut();
}
SomethingProvider:
public class SomethingProvider : ISomethingProvider
{
// TODO: Implement methods in ISomethingProvider, once they have been added
}
SomethingProviderInstaller:
public class SomethingProviderInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn(typeof(ISomethingProvider))
.WithServiceAllInterfaces());
// From http://app-code.net/wordpress/?p=676; see also http://devlicio.us/blogs/krzysztof_kozmic/archive/2009/12/24/castle-typed-factory-facility-reborn.aspx
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IMyFirstFactory>().AsFactory());
}
}
Controller:
public class DepartmentsController : ApiController
{
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
public int GetCountOfDepartmentRecords()
{
return _deptsRepository.Get();
}
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch)
{
return _deptsRepository.Get(ID, CountToFetch);
}
. . .
}
IRepository:
public interface IDepartmentRepository
{
int Get();
IEnumerable<Department> Get(int ID, int CountToFetch);
}
Repository:
public class DepartmentRepository : IDepartmentRepository
{
private readonly List<Department> departments = new List<Department>();
public DepartmentRepository()
{
using (var conn = new OleDbConnection(
#"Provider=Microsoft.ACE.OLEDB.12.0;[bla]"))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT td_department_accounts.dept_no, IIF(ISNULL(t_accounts.name),'No Name provided',t_accounts.name) AS name FROM t_accounts INNER JOIN td_department_accounts ON t_accounts.account_no = td_department_accounts.account_no ORDER BY td_department_accounts.dept_no";
cmd.CommandType = CommandType.Text;
conn.Open();
int i = 1;
using (OleDbDataReader oleDbD8aReader = cmd.ExecuteReader())
{
while (oleDbD8aReader != null && oleDbD8aReader.Read())
{
int deptNum = oleDbD8aReader.GetInt16(0);
string deptName = oleDbD8aReader.GetString(1);
Add(new Department { Id = i, AccountId = deptNum, Name = deptName });
i++;
}
}
}
}
}
public int Get()
{
return departments.Count;
}
private Department Get(int ID) // called by Delete()
{
return departments.First(d => d.Id == ID);
}
public IEnumerable<Department> Get(int ID, int CountToFetch)
{
return departments.Where(i => i.Id > ID).Take(CountToFetch);
}
. . .
}
Global.asax.cs:
public class WebApiApplication : System.Web.HttpApplication
{
private static IWindsorContainer container;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
BootstrapContainer();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private static void BootstrapContainer()
{
container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot(container));
}
protected void Application_End()
{
container.Dispose();
}
UPDATE
In trying to run the server, so that it could test it with Fiddler2 to see just what is being passed around, it failed on this line in WindsorControllerFactory:
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
kernel.AddFacility<TypedFactoryFacility>(); <-- throws exception here
}
...with "System.ArgumentException was unhandled by user code
HResult=-2147024809
Message=Facility of type 'Castle.Facilities.TypedFactory.TypedFactoryFacility' has already been registered with the container. Only one facility of a given type can exist in the container.
Source=Castle.Windsor
StackTrace:
at Castle.MicroKernel.DefaultKernel.AddFacility(String key, IFacility facility)
at Castle.MicroKernel.DefaultKernel.AddFacility(IFacility facility)
at Castle.MicroKernel.DefaultKernel.AddFacilityT
at HandheldServer.DIPlumbing.WindsorControllerFactory..ctor(IKernel kernel) in c:\HandheldServer\HandheldServer\DIPlumbing\WindsorControllerFactory.cs:line 28
at HandheldServer.WebApiApplication.BootstrapContainer() in c:\HandheldServer\HandheldServer\Global.asax.cs:line 69
at HandheldServer.WebApiApplication.Application_Start() in c:\HandheldServer\HandheldServer\Global.asax.cs:line 39"
UPDATE 2
In response to Cristiano's answer:
So are you saying I should add the following two files to my Installers folder (I do have a DIInstallers folder already)
PlatypusInstallerFactory.cs:
public class PlatypusInstallerFactory : InstallerFactory
{
public override IEnumerable<Type> Select(IEnumerable<Type> installerTypes)
{
var windsorInfrastructureInstaller = installerTypes.FirstOrDefault(it => it == typeof(WindsorInfrastructureInstaller));
var retVal = new List<Type>();
retVal.Add(windsorInfrastructureInstaller);
retVal.AddRange(installerTypes
.Where(it =>
typeof(IWindsorInstaller).IsAssignableFrom(it) &&
!retVal.Contains(it)
));
return retVal;
}
}
WindsorInfrastructureInstaller.cs:
public class WindsorInfrastructureInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<TypedFactoryFacility>();
}
}
In your global.asax you'll create&use you installer factory as following
var installerFactory = new PlatypusInstallerFactory();
container.Install(FromAssembly.This(installerFactory));
If yes, what will that do for me? Does the above automagically register my Controller and/or Repository classes?
UPDATE 3
I am now using a lot of code from [http://blog.kerbyyoung.com/2013/01/setting-up-castle-windsor-for-aspnet.html#comment-form]
The key parts are, I think:
global.asax.cs:
private static IWindsorContainer _container;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureWindsor(GlobalConfiguration.Configuration);
}
public static void ConfigureWindsor(HttpConfiguration configuration)
{
_container = new WindsorContainer();
_container.Install(FromAssembly.This());
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
}
WindsorDependencyResolver.cs:
namespace HandheldServer
{
public class WindsorDependencyResolver : System.Web.Http.Dependencies.IDependencyResolver
{
private readonly IWindsorContainer _container;
public WindsorDependencyResolver(IWindsorContainer container)
{
_container = container;
}
public IDependencyScope BeginScope()
{
return new WindsorDependencyScope(_container);
}
public object GetService(Type serviceType)
{
if (!_container.Kernel.HasComponent(serviceType))
{
return null;
}
return this._container.Resolve(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (!_container.Kernel.HasComponent(serviceType))
{
return new object[0];
}
return _container.ResolveAll(serviceType).Cast<object>();
}
public void Dispose()
{
_container.Dispose();
}
}
public class WindsorDependencyScope : IDependencyScope
{
private readonly IWindsorContainer _container;
private readonly IDisposable _scope;
public WindsorDependencyScope(IWindsorContainer container)
{
this._container = container;
this._scope = container.BeginScope();
}
public object GetService(Type serviceType)
{
if (_container.Kernel.HasComponent(serviceType))
{
return _container.Resolve(serviceType);
}
else
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this._container.ResolveAll(serviceType).Cast<object>();
}
public void Dispose()
{
this._scope.Dispose();
}
}
public class ApiControllersInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly() // should it be Types instead of Classes?
.BasedOn<ApiController>()
.LifestylePerWebRequest());
}
}
// This idea from https://github.com/argeset/set-locale/blob/master/src/client/SetLocale.Client.Web/Configurations/IocConfig.cs
public class ServiceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IDeliveryItemRepository>().ImplementedBy<DeliveryItemRepository>().LifestylePerWebRequest(),
Component.For<IDeliveryRepository>().ImplementedBy<DeliveryRepository>().LifestylePerWebRequest(),
Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository>().LifestylePerWebRequest(),
Component.For<IExpenseRepository>().ImplementedBy<ExpenseRepository>().LifestylePerWebRequest(),
Component.For<IInventoryItemRepository>().ImplementedBy<InventoryItemRepository>().LifestylePerWebRequest(),
Component.For<IInventoryRepository>().ImplementedBy<InventoryRepository>().LifestylePerWebRequest(),
Component.For<IItemGroupRepository>().ImplementedBy<ItemGroupRepository>().LifestylePerWebRequest());
}
}
}
UPDATE 4
This question is probably too general for SO, so I posted it on "Programmers"
UPDATE 5
Note: According to "The DI Whisperer" (Mark Seemann), IDependencyResolver should not be used, because it lacks a Release method (p. 207 of his book)
You should not mix installation vs resolving.
IOW your should not have
kernel.AddFacility<TypedFactoryFacility>();
in the WindsorControllerFactory
But the generic container configuration such registering TypedFactoryFacility should be executed in an installer called as earlier as possible.
In order to drive installer execution, you should use an Installer factory
public class YourInstallerFactory : InstallerFactory
{
public override IEnumerable<Type> Select(IEnumerable<Type> installerTypes)
{
var windsorInfrastructureInstaller = installerTypes.FirstOrDefault(it => it == typeof(WindsorInfrastructureInstaller));
var retVal = new List<Type>();
retVal.Add(windsorInfrastructureInstaller);
retVal.AddRange(installerTypes
.Where(it =>
typeof(IWindsorInstaller).IsAssignableFrom(it) &&
!retVal.Contains(it)
));
return retVal;
}
}
Where windsorInfrastructureInstaller will be somenthing like this
public class WindsorInfrastructureInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// Resolvers
//container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
// TypedFactoryFacility
container.AddFacility<TypedFactoryFacility>();
}
}
In your global.asax you'll create&use you installer factory as following
var installerFactory = new YourInstallerFactory();
container.Install(FromAssembly.This(installerFactory));
Your "FrontEnd"(for example the mvc/webapi) project has a folder containing all installers(WindsorInfrastructureInstaller will be one of those) and the installer factory as well or at least that's the way I'm use to organize my solution.
In answer to my own question, I would simply say: There are no shortcakes! Without stopping go or further ado, go here and get this book. Resign yourself to take the time necessary to read it carefully.
So I'm not the only one; here's a quote from Jeff Beck, who wrote that book's foreword: "Often those who start using DI quickly find themselves lost in a sea of confusion."
Don't want to repeat everything again, so just check out my answer on How do I get Web API / Castle Windsor to recognize a Controller?.
As another note - I would advise against doing anything in your repository constructors if you can help it. The reason I say this is that the constructors get called as Windsor is trying to instantiate the correct instance of your repository. What this means is that if any kind of error occurs, it happens as WebApi is trying to create the controller. This can make it a bit tricky to track down the problem sometimes, and also ends up hiding the real issues under tons of layers of exceptions.
I'm using ASP.NET MVC with Castle Windsor as my IoC container with the component lifestyle set to PerWebRequest. My repository (which is the dependency that's injected) creates an instance of Entity Framework's ObjectContext in the constructor and I store that in a private instance variable. My repository implements IDisposable and inside my Dispose method, I dispose the ObjectContext. I think all of this is pretty standard and here's a simplified illustration:
Repository:
public class Repository : IRepository {
private MyContext _dc; // MyContext inherits from ObjectContext
public Repository() {
_dc = new MyContext();
}
public void Dispose() {;
_dc.Dispose();
}
}
To ensure that there's no memory leak and that my Repository's Dispose() is called, I override DefaultControllerFactory's ReleaseController method to release Windsor's container:
public class WindsorControllerFactory : DefaultControllerFactory {
IWindsorContainer _container;
public WindsorControllerFactory(IWindsorContainer container) {
_container = container;
// Do stuff to register all controller types
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
// Do stuff to resolve dependency
}
public override void ReleaseController(IController controller) {
// by releasing the container, Windsor will call my Dispose() method on my repository
_container.Release(controller);
base.ReleaseController(controller);
}
}
I think all of this is pretty standard. However, I'd like to spin off a parallel thread, and inside that parallel thread utilize the IRepository dependency. My problem is that my repository will have already been disposed by the time I use it:
public class HomeController : Controller {
IRepository _repository;
public HomeController(IRepository repository) {
_repository = repository;
}
public ActionResult Index() {
var c = _repository.GetCompany(34);
new Task(() => {
System.Threading.Thread.Sleep(2000); // simulate long running task
// will throw an error because my repository (and therefore, ObjectContext) will have been disposed.
var c2 = _repository.GetCompany(35);
}).Start();
return Content(c.Name, "text/plain");
}
}
How do other people solve this problem? How do you pass your dependencies to a parallel thread?
Thanks in advance.
Create a new Repository instance. ObjectContexts are inexpensive to construct.
OR, you could attach the responsibility of disposing the Repository to the long running thread. I messed around with it, I think these alterations to your code will solve your problem:
In your WindsorControllerFactory
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
IController controller;
// Do stuff to resolve dependency
if(controller is LongTaskController)
{
((LongTaskController) controller).CompleteLongTask += (sender, args) => _container.Release(controller);
}
}
public override void ReleaseController(IController controller)
{
// by releasing the container, Windsor will call my Dispose() method on my repository
if(!(controller is LongTaskController && ((LongTaskController)controller).HasLongTask)
{
_container.Release(controller);
}
base.ReleaseController(controller);
}
In HomeController
public class HomeController : LongTaskController
{
private readonly IRepository _repository;
public HomeController(IRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
var c = _repository.GetCompany(34);
DoLongTask(() =>
{
Thread.Sleep(200);
var c2 = _repository.GetCompany(35);
});
return Content(c.Name, "text/plain");
}
}
And the new base controller
public abstract class LongTaskController: Controller,IHasLongTask
{
private bool _hasLongTask;
public bool HasLongTask { get { return _hasLongTask; } }
public event EventHandler CompleteLongTask;
void IHasLongTask.DoLongTask(Action action) { DoLongTask(action); }
protected void DoLongTask(Action action)
{
_hasLongTask = true;
if (CompleteLongTask == null)
{
throw new NullReferenceException("Controller.CompleteLongTask cannot be null when Controller does a long running task.");
action += () => CompleteLongTask(this, EventArgs.Empty);
}
new Task(action).Start();
}
}
public interface IHasLongTask
{
bool HasLongTask { get; }
void DoLongTask(Action action);
event EventHandler CompleteLongTask;
}
I watched the first 2 beginner tutorials for Ninject on dimecasts.net. Now, I want to use Ninject 2.2 in ASP.NET MVC 3. I want a view with a mocked out Model. I get object reference not set to an instance of an object when calling my service;
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController()
{
}
HomeController(IMilestoneService service)
{
_service = service;
}
public ActionResult Index()
{
ViewBag.Message = "Change Request System";
return View();
}
public ActionResult About()
{
return View();
}
#region Partial views
public ActionResult Milestone()
{
var result = _service.GetMileStones();//OBJECT REF ERROR
return View(result);
}
#endregion
}
//####GLOBAL.ASAX
//By using the NinjectHttpApplication, it automatically takes care of controllers, starting up mvc, etc.
//Ninject.Web.Mvc
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//StartNinject();
}
#region Inversion of Control
protected override IKernel CreateKernel()
{
return Container;
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
{
_container = new StandardKernel(new SiteModule());
}
return _container;
}
}
internal class SiteModule : NinjectModule
{
public override void Load()
{
//Set up ninject bindings here.
Bind<IMilestoneService>().To<MileStoneService>();
}
}
#endregion
}
I'm using Razor, he's the milestone partial view
#foreach (var item in Model)
{
<div>item.Name</div>
}
Finally, the Home view Index
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
<p>
#Html.Action("Milestone");
</p>
Edit 11/20/2013
Note that Ninject has since released version 2.0. The changes are nicely outlined on their site. Of Note StandardModule is now NinjectModule and namespace Ninject.Core no longer exists. I was able to replace it with just Ninject.
There is an issue with your controller class, the constructor with the dependency is private. Your controller should look like:
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController(IMilestoneService service)
{
_service = service;
}
}
Don't even include a public parameterless constructor, it isn't even valid, your class needs that dependency to function.
In fact, I also insert a null check against that dependency in the constructor just to be sure my class is valid on construction:
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController(IMilestoneService service)
{
_service = service;
Enforce.NotNull(() => _service); // lambda to auto-magically get variable name for exception
}
}
There also may be an issue with your MvcApplication class.
Instead of protected void Application_Start(), there is a different function you can override, protected override void OnApplicationStarted()
This is where your calls to setup routing should go:
public class MvcApplication : NinjectHttpApplication
{
public override void Init()
{
base.Init();
Mappers.Initialize();
}
protected override Ninject.IKernel CreateKernel()
{
return Ioc.Initialize();
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
Routing.RegisterRoutes(routes);
//RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}
}
Of course, if you are already calling Application_Start that's fine too, but I didn't see it in the OP.
Try this in your global.asax file:
//By using the NinjectHttpApplication, it automatically takes care of controllers, starting up ninject, etc.
//Ninject.Web.Mvc
public class MvcApplication : NinjectHttpApplication
{
//Your other stuff here. No need to call StartNinject().
#region Inversion of Control
protected override IKernel CreateKernel()
{
return Container;
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
{
_container = new StandardKernel(new SiteModule());
}
return _container;
}
}
internal class SiteModule : NinjectModule
{
public override void Load()
{
//Set up ninject bindings here.
Bind<IMilestoneService>().To<MileStoneService>();
}
}
#endregion
}
I believe if Ninject couldn't bind that interface, you'd get a binding error. This makes me think Ninject is not instantiating your controller.
Have you included Ninject.Web.Mvc?