I am running into an issue when using my Castle Windsor Controller Factory with the new RenderAction method. I get the following error message:
A single instance of controller 'MyController' cannot be used to handle multiple requests. If a custom controller factory is in use, make sure that it creates a new instance of the controller for each request.
This is the code in my controller factory:
public class CastleWindsorControllerFactory : DefaultControllerFactory
{
private IWindsorContainer container;
public CastleWindsorControllerFactory(IWindsorContainer container)
{
this.container = container;
}
public override IController CreateController(RequestContext requestContext, string controllerName)
{
return container.Resolve(controllerName) as IController;
}
public override void ReleaseController(IController controller)
{
this.container.Release(controller);
}
}
Does anyone know what changes I need to make to make it work with RenderAction?
I also find the error message slightly strange because it talks about multiple requests, but from what I can tell RenderAction doesn't actually create another request (BeginRequest isn't fired again).
I believe the default config for Castle Windsor is a Singleton. You need to change this to Transient in your Web.Config or by putting this attribute on your class [Transient].
Related
I have a Web Api project and using Castle Windsor as the IoC. I have done this quite a few times and honestly I cannot understand why this is not working so here goes everything that I am doing:
Controller on the Web Api project:
Nothing fancy here
public class TestController : ApiController, ITestController
{
... //Currently with its default constructor.
}
Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
private readonly IWindsorContainer container;
public WebApiApplication()
{
container = new WindsorContainer().Install(FromAssembly.Named("DependenciesConfiguration"));
}
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new DiControllerActivator(container));
}
}
DiControllerActivation class
This is the class that is replacing the default controller activator in the Global.asax class.
public class DiControllerActivator : IHttpControllerActivator
{
private readonly IWindsorContainer container;
public DiControllerActivator(IWindsorContainer container)
{
this.container = Argument.NotNull(container, (nameof(container)));
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
//This is the line that fails saying "No component for supporting the service TestController was found.
var controller = (IHttpController)container.Resolve(controllerType);
request.RegisterForDispose(new Release(() => container.Release(controller)));
return controller;
}
}
internal class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
And finally the Web Services Installer
public class ServicesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromAssemblyNamed("WebServices")
.Where(type => type.Name.EndsWith("Controller"))
.WithService.DefaultInterfaces()
.Configure(c => c.LifestylePerWebRequest()));
}
}
What it is really frustrating is that, like I said, I have donde this before, I went to my previous work to see if I was missing anything and I cannot find anything and this has always worked well.
When running the application after the installer runs I can see that the service is in the container and when it fails saying "No component for supporting the service TestController was found" I can still see the service within the container in the Watch Window.
I am using Castle version 4.0
Thank you for any pointers and help.
As it turns out there is a subtle but critical difference in this code from the other times I did this.. The fact that the TestController is implementing an interface and is registered as such messes up the whole thing.
Since the Create method in the DiActivator class is trying to resolve the controller based on its type and not its default interface the container cannot find it.
One solution is just to remove the interface from the controller or to add the necessary code in the Create method that gets the default interface of the received type and resolves for it.
I'm trying to Inject a dependency into a controller which inherits from Umbraco's RenderMvcController and getting the error
No registration for type RenderMvcController could be found and an implicit registration could not be made. For the container to be able to create RenderMvcController it should have only one public constructor: it has 3. See https://simpleinjector.org/one-constructor for more information.
Below is my code to wire up the DI
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
InitializeContainer(container);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
private static void InitializeContainer(Container container)
{
container.Register<ICacheProvider, CacheProvider>(Lifestyle.Transient);
container.Register<ICacheService, CacheService>(Lifestyle.Transient);
}
This is an example of a class receiving the dependency, it inherits from a base class I wrote
public class NewsroomController : BaseRenderMvcController
{
public NewsroomController(ICacheService cacheService) : base(cacheService) { }
The base class extends RenderMvcController which is an Umbraco Controller
public class BaseRenderMvcController : RenderMvcController
{
public ICacheService CacheService { get; set; }
public BaseRenderMvcController(ICacheService cacheService)
{
CacheService = cacheService;
}
}
As you can see the base Umbraco controller does in fact have 3 different constructors
public class RenderMvcController : UmbracoController, IRenderMvcController, IRenderController, IController
{
public RenderMvcController();
public RenderMvcController(UmbracoContext umbracoContext);
public RenderMvcController(UmbracoContext umbracoContext, UmbracoHelper umbracoHelper);
I'm not sure how to get SimpleInjector to place nicely with this controller inherited from Umbraco.
Thanks in advance!
The exception message "No registration for type RenderMvcController could be found and an implicit registration could not be made" means that the RenderMvcController type is requested directly from Simple Injector, while it hasn't been registered. A controller type is usually only requested by the DefaultControllerFactory and it will only request a specific type when it gets a request that has the name of the controller in its url, as in: http:\\localhost\RenderMvc\3.
Since you stated in the comments that the RenderMvcController is only meant to be used as base controller, I find it suspicious that it is actually requested by MVC. I think you should look into that.
But it the use of this controller is really required, you can simply register it in Simple Injector like this:
container.Register<RenderMvcController>(() => new RenderMvcController());
There are ways to override Simple Injector's constructor resolution behavior, but I would advise against doing this, because it is an anti-pattern for components to have multiple constructors. It's wise to don't use a container's auto-wiring behavior on framework types (as explained here), so registering them using a lambda is the advised practice.
I have class, which I use to register as GlobalFilter to handle execpions. I register it on global.asax, there everything is all right. When exception occurs in my action, I enter in OhException method and get NullReference exception, becouse LogManager property is null.Why nInject doesn't inject it?
public class HandleErrorFilterAttribute : HandleErrorAttribute
{
[Inject]
public ILogManager LogManager { get; set; }
public override void OnException(ExceptionContext filterContext)
{
LogManager.LogError("Unhandled exception thrown", filterContext.Exception);
base.OnException(filterContext);
}
}
This code below service injection (I forgot write about it)
kernel.Bind<ILogManager>().To<LogManager>();
And there I create object
public void RegisterGlobalFilters()
{
var handleErrorAttribute = new HandleErrorFilterAttribute();
GlobalFilters.Filters.Add(handleErrorAttribute);
}
By default, attributes are created by the CLR, not by your DI library. A DI library can only inject dependencies in objects that it creates itself, or when you tell it explicitly to 'build up' this object. So in case you new up objects yourself or let the CLR create attributes, the DI library has no chance in injecting dependencies into it.
Does anyone have any example code for getting Umbraco MVC working with the Castle Windsor dependency injection framework? The problem I'm having is getting my surface controllers to use injectable parametised constructors. I know I'm doing something wrong but not sure what.
I have followed the (non-Umbraco) tutorial here - http://docs.castleproject.org/Windsor.Windsor-tutorial-part-four-putting-it-all-together.ashx - which basically means on App_Start I'm running this code:
var container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new MyCustomControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
Code for MyCustomControllerFactory is below.
Also, my implementation of IWindsorInstaller contains the following:
container.Register(Classes.FromThisAssembly()
.BasedOn<SurfaceController>()
.LifestyleTransient());
The exception I'm getting is 'No component for supporting the service Umbraco.Web.Mvc.RenderMvcController was found', thrown by the GetControllerInstance method below when I call a surface controller with a parametised constructor:
public class TestSurfaceController : SurfaceController
{
public TestSurfaceController(INameService nameService)
{
....
}
}
If anyone has some example code which works I'd really appreciate it. I've wired up Ninject with Umbraco before with no trouble, but on this project I'm tied to Castle Windsor and getting nowhere fast! Thanks in advance.
MyCustomControllerFactory.cs:
public class MyCustomControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public FastStartControllerFactory(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 new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)kernel.Resolve(controllerType);
}
}
I believe your problem is here:
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
This is replacing the controller factory for ALL controllers, including the RenderMVCController, and Castle can't find a matching component for that type.
The trick is to use the FilteredControllerFactoryResolver, which lets Umbraco decide which controller to use based on some criteria that you provide (in this case, whether your container can resolve the controller type). Composition is not as clean as in a straight MVC app (IMHO), but it works.
Here's an (Umbraco 7.x) example of a filtered controller that implements the IFilteredControllerFactory interface:
public class FilteredControllerFactory : ControllerFactory, IFilteredControllerFactory
{
public bool CanHandle(RequestContext request)
{
Type controllerType = GetControllerType(request, request.RouteData.Values["controller"].ToString());
return ApplicationStartup.Container.Kernel.HasComponent(controllerType);
}
}
And the corresponding code to set up composition (using ApplicationEventHandler):
public class ApplicationStartup : ApplicationEventHandler
{
internal static IWindsorContainer Container;
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
base.ApplicationStarting(umbracoApplication, applicationContext);
Container = new WindsorContainer()
.Install(Configuration.FromAppConfig())
.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
FilteredControllerFactoriesResolver.Current.InsertType<FilteredControllerFactory>(0);
}
}
This approach should work both for route hijacking and for surface controllers.
Finally, note that if you also want to support injection into API controllers, you'll need to wire this up separately. For example:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new CompositionRoot(Container.Kernel))
where CompositionRoot is your own Windsor composition root class.
The Gist here may also prove useful.
I've read Kristopher's answer and I've found it interesting, because I didn't know IFilteredControllerFactory and its use. Thanks for sharing.
Anyway, usually in my projects I have a lot of dll containing each its own controllers, so I prefer to register all the controllers in a more general way:
container.Register(
Classes
.FromAssemblyInDirectory(new AssemblyFilter(AssemblyDirectory))
.BasedOn<IController>()
.LifestyleTransient());
where
/// <summary>
/// Local Directory where are present all the assemblies
/// </summary>
static public string AssemblyDirectory
{
//Snippet code from: https://gist.github.com/iamkoch/2344638
get
{
var codeBase = Assembly.GetExecutingAssembly().CodeBase;
var uri = new UriBuilder(codeBase);
var path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}
In this way also the Umbraco's RenderMVCController will be mapped and correctly resolved.
Recently I wrote a couple of articles about DI in a Umbraco app:
MVC Controller Factory
Properties Injection in MVC Filters
Hope it can help
I created a custom controller factory to be able to inject service instances to my controllers using StructureMap.
Everything works fine with the exception that with every request the controller factory is called a first time in which it resolves the controller properly and a second time in which the controllerType parameter is null and so StructureMap's GetInstance method throws an ArgumentNullException: Value cannot be null. Parameter name: key.
The application actually does not crash but if I'm debugging it always stops there and I have to manually continue the execution so the view gets displayed.
Could anyone please explain why this is happening and how could I solve it.
Here is the code from both my Global.asax and the controller factory:
Controller Factory:
public class IocControllerFactory : DefaultControllerFactory
{
private readonly IContainer container;
public IocControllerFactory(IContainer container)
{
if(container == null) throw new ArgumentNullException("container");
this.container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return this.container.GetInstance(controllerType) as IController;
}
}
Global.asax
private void RegisterControllerFactory()
{
var ioc = new Container();
var controllerFactory = new IocControllerFactory(ioc);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
ioc.Configure(r =>
r.Scan(x =>
{
x.AssemblyContainingType<UserAccountController>();
x.AddAllTypesOf<IController>();
x.Include(t => typeof(IController).IsAssignableFrom(t));
}
));
ioc.Configure(r => r
.For<IUserAccountService>()
.Use<UserAccountService>());
}
Thank you so much for any help.
Remember that ASP.NET MVC punches every request that does not map to a file through the controller factory with the default configurations. And most browsers request a favicon.ico file by default. So, what is happening is your favicon is getting called but that don't map to a type so StructureMap is getting a null type and erroring out.
Easiest fixes are to add a favicon.ico file or to add an ignore for the route.