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.
Related
I have read many other SO questions on the same topic, but none of the answers that I found applies to my case.
I have successfully added 4 services in my Startup.cs, and it was working fine before. I then added the 5th, and now I realize that something is broken - none of the services work. Even if I remove the 5th completely, the other ones are now also broken with the same error.
Unable to resolve service for type xx while attempting to activate
This is my Startup.cs ConfigureServices.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddStorage();
services.AddSingleton<IMyLocalStorage, MyLocalStorage>();
services.AddSingleton<IFrontEndService, FrontEndService>();
services.AddSingleton<ISystemProvider, SystemProviderService>();
services.AddSingleton<IAuthenticationService, AuthenticationService>();
}
It's the last AuthenticationService that I noticed the error, but even the older previously working services fails now.
public interface IAuthenticationService
{
// ...
}
public class AuthenticationService : IAuthenticationService
{
private readonly FrontEndService frontEndService;
private readonly MyLocalStorage myLocalStorage;
public AuthenticationService(FrontEndService frontEndService, MyLocalStorage myLocalStorage)
{
this.frontEndService = frontEndService;
this.myLocalStorage = myLocalStorage;
}
// ...
}
The services are simple; one interface, one implementation of that interface, and then adding in Startup.cs. I can't figure out why it stopped working.
So if I remove IAuthenticationService, then the error instead shows up in FrontEndService, then complaining on the MyLocalStorage:
public interface IFrontEndService
{
Task<T> GetAsync<T>(string requestUri);
}
public class FrontEndService : IFrontEndService
{
private readonly HttpClient client;
private readonly MyLocalStorage myLocalStorage;
public FrontEndService(HttpClient client, MyLocalStorage myLocalStorage)
{
// ...
}
}
and
public class MyLocalStorage : IMyLocalStorage
{
public MyLocalStorage(LocalStorage storage)
{
this.storage = storage;
}
}
What am I missing here?
When you call methods on IServiceCollection such as .AddSingleton<IFrontEndService, FrontEndService>(), you're saying to the container, "Whenever you see an IFrontEndService dependency, inject an instance of FrontEndService." Now if you take a look at your AuthenticationService:
public class AuthenticationService : IAuthenticationService
{
private readonly FrontEndService frontEndService;
private readonly MyLocalStorage myLocalStorage;
public AuthenticationService(FrontEndService frontEndService, MyLocalStorage myLocalStorage)
{
this.frontEndService = frontEndService;
this.myLocalStorage = myLocalStorage;
}
// ...
}
Notice how you're passing in dependencies of FrontEndService and MyLocalStorage, rather than the interfaces you registered. That means the container doesn't recognise them, so it doesn't know how to fulfil the dependency graph.
You need to change the service to depend on the interfaces, as those are what you've registered with the container:
public class AuthenticationService : IAuthenticationService
{
private readonly IFrontEndService frontEndService;
private readonly IMyLocalStorage myLocalStorage;
public AuthenticationService(IFrontEndService frontEndService, IMyLocalStorage myLocalStorage)
{
this.frontEndService = frontEndService;
this.myLocalStorage = myLocalStorage;
}
// ...
}
#Ted,
Do you remember a question of yours from a couple of weeks ago, in which you used LocalStorage in a service ? At that service you had a constructor with IStorage parameter, but this caused an error, the reason of which was that though the LocalStorage class implements the IStorage interface, the creators of this library added the LocalStorage to the DI container as a concrete class like this:
public static IServiceCollection AddStorage(this IServiceCollection services)
{
return services.AddSingleton<SessionStorage>()
.AddSingleton<LocalStorage>();
}
And therefore, you had to use
(LocalStorage storage)
instead of
(IStorage storage)
The extension method above, could be rewritten thus:
public static IServiceCollection AddStorage(this IServiceCollection services)
{
return services.AddSingleton<IStorage, SessionStorage>()
.AddSingleton<IStorage, LocalStorage>();
}
In which case, you could use the IStorage interface in your constructor.
Now you may form a general rule, and act accordingly.
Ted says:
Thats odd, cause I have used exactly this approach before, and it
worked fine. If you read the docs, Microsoft also uses the concrete
class, not the interface
HttpClient derives from HttpMessageInvoker. It does not implement any interface.
This code-snippet shows how the HttpClient is added to the service container, and made available for injection in your client-side Blazor:
services.AddSingleton<HttpClient>(s =>
{
// Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
var uriHelper = s.GetRequiredService<IUriHelper>();
return new HttpClient
{
BaseAddress = new Uri(WebAssemblyUriHelper.Instance.GetBaseUri())
};
});
Hope this helps...
Trying to do some DI on Web API 2 without third-party tools.
So, from some examples I've got custom dependency resolver (why there's no integrated one? Strange, even Microsoft.Extensions.DependencyInjection provides nothing):
public class DependencyResolver : IDependencyResolver
{
protected IServiceProvider _serviceProvider;
public DependencyResolver(IServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
public IDependencyScope BeginScope()
{
return this;
}
public void Dispose()
{
}
public object GetService(Type serviceType)
{
return this._serviceProvider.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this._serviceProvider.GetServices(serviceType);
}
public void AddService()
{
}
}
then created this class:
public class ServiceConfig
{
public static void Register(HttpConfiguration config)
{
var services = new ServiceCollection();
services.AddScoped<IMyService, MyServiceClient>();
var resolver = new DependencyResolver(services.BuildServiceProvider());
config.DependencyResolver = resolver;
}
}
and registered it:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configure(ServiceConfig.Register);
}
But when I'm trying to use it:
public class TestController : ApiController
{
private IMyService _myService = null;
public TestController(IMyService myService)
{
_myService = myService;
}
public void Get()
{
_myService.DoWork();
}
}
I'm getting error:
An error occurred when trying to create a controller of type 'TestController'. Make sure that the controller has a parameterless public constructor.
How to cook this one in right way?
What you see happening is related to this problem. In short, Web API will call its default IHttpControllerActivator implementation to request a new controller instance. That instance will call into your DependencyResolver.GetService method. That method will forward the call to MS.DI's GetService method. However, since you didn't register your controllers into the MS.DI container, it will return null. This will cause the default IHttpControllerActivator to try to create the controller using reflection, but this requires a default constructor. Since the controller doesn't have one, this results in the rather cryptic exception message.
The quick solution, therefore, is to register your controllers, e.g.:
services.AddTransient<TestController>();
This, however, will only partly solve your problem because your IDependencyResolver implementation is broken. It is broken in an ugly way, because it might seem to work at first, but will result in memory leaks, because you always resolve from the root container, instead of resolving from a scope. This will cause your resolved controller instances (and other disposable transient components) to stay referenced for the lifetime of your application.
To fix this, you should change your IDependencyResolver implementation to the following:
public class DependencyResolver : IDependencyResolver
{
private readonly IServiceProvider provider;
private readonly IServiceScope scope;
public DependencyResolver(ServiceProvider provider) => this.provider = provider;
internal DependencyResolver(IServiceScope scope)
{
this.provider = scope.ServiceProvider;
this.scope = scope;
}
public IDependencyScope BeginScope() =>
new DependencyResolver(provider.CreateScope());
public object GetService(Type serviceType) => provider.GetService(serviceType);
public IEnumerable<object> GetServices(Type type) => provider.GetServices(type);
public void Dispose() => scope?.Dispose();
}
This implementation will ensure a new IServiceScope is created on each web request and services are always resolved from a request; not from the root IServiceProvider.
Although this will fix your problems, another implementation might still be benificial.
The IDependencyResolver contract is problematic, because it is forced to return null when a call to GetService doesn't result in the correct resolution of a registration. This means that you will end up with these annoying "Make sure that the controller has a parameterless public constructor" errors when you forget to register your controllers.
It is, therefore, much easier to create a custom IHttpControllerActivator instead. In that case you can call GetRequiredService which will never return null:
public class MsDiHttpControllerActivator : IHttpControllerActivator
{
private readonly ServiceProvider provider;
public MsDiHttpControllerActivator(ServiceProvider provider) =>
this.provider = provider;
public IHttpController Create(
HttpRequestMessage request, HttpControllerDescriptor d, Type controllerType)
{
IServiceScope scope = this.provider.CreateScope();
request.RegisterForDispose(scope); // disposes scope when request ends
return (IHttpController)scope.ServiceProvider.GetRequiredService(controllerType);
}
}
This MsDiHttpControllerActivator implementation can be added to the Web API pipeline as follows:
GlobalConfiguration.Configuration.Services
.Replace(typeof(IHttpControllerActivator),
new MsDiHttpControllerActivator(services.BuildServiceProvider(true)));
This removes the need to have an IDependencyResolver implementation. You still need to register your controllers, though:
services.AddTransient<TestController>();
Also note that I changed this:
services.BuildServiceProvider()
To this:
services.BuildServiceProvider(true)
This is a really important change; it protects you (for some part) against Captive Dependencies, which are one of the major problems when using DI Containers. For some obscure reason, the BuildServiceProvider() overload defaults to false, which means it will not validate your scopes.
My current Autofac config is working to resolve my ApiControllers in a WebApi.
Where I'm struggling is, I'm attempting to create a 'BaseApiController' with generic constructor parameters, but getting exception:
No constructors on type 'Service`1[WorldRegion]' can be found with the constructor finder 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'.
Here is the code structure:
public interface IEntityKey { }
public class WorldRegion : IEntityKey { }
public interface IRepository<T> where T : IEntityKey { }
public interface IService<T> where T : IEntityKey { }
public abstract class Service<T> : IService<T> where T : IEntityKey { }
public interface IWorldRegionService : IService<WorldRegion> { }
public class WorldRegionService : Service<WorldRegion>, IWorldRegionService
{
private readonly IRepository<WorldRegion> _repository;
}
WORKING API Controller:
public class WorldRegionsController : BaseApiController
{
private readonly IWorldRegionService _worldRegionService;
private readonly ICultureService _cultureService;
public WorldRegionsController(IWorldRegionService worldRegionService, ICultureService cultureService)
{
_worldRegionService = worldRegionService;
_cultureService = cultureService;
}
}
WORKING Autofac config:
public static void Register(HttpConfiguration config, IAppBuilder app)
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
RegisterTypes(builder);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
}
public static void RegisterTypes(ContainerBuilder builder)
{
// Context
builder.RegisterType<DataContext>().As<IDataContext>().InstancePerRequest();
// UOW
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
// Repositories
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerRequest();
// Services
builder.RegisterType<CultureService>().As<ICultureService>().InstancePerRequest();
builder.RegisterType<WorldRegionService>().As<IWorldRegionService>().InstancePerRequest();
}
Here is the generic ATTEMPT:
// BaseApiController
public abstract class BaseApiController<T> : ApiController where T : IEntityKey
{
protected readonly IService<T> _service;
protected readonly ICultureService _cultureService;
public BaseApiController(IService<T> service, ICultureService cultureService)
{
_service = service;
_cultureService = cultureService;
}
}
// ApiController
public class WorldRegionsController : BaseApiController<WorldRegion>
{
public WorldRegionsController(
IService<WorldRegion> service, ICultureService cultureService)
: base(service, cultureService) {}
}
// Added to Autofac config
builder.RegisterGeneric(typeof(Service<>)).As(typeof(IService<>)).InstancePerRequest();
// Removed
builder.RegisterType<WorldRegionService>().As<IWorldRegionService>().InstancePerRequest();
With this change, I'm getting the exception message noted above (in yellow). I think I'm missing something in the Autofac config, just not sure what. Maybe include/register/add 'WorldRegion' somehow.
How should I register my types ?
Your controller expect a IService<WorldRegion>. Autofac find the following registration for this service :
builder.RegisterGeneric(typeof(Service<>)).As(typeof(IService<>)).InstancePerRequest();
So it tries to create a Service<WorldRegion> which is not possible because Service<T> is an abstract class.
Don't forget that a IWorldRegionService is a IService<WorldRegion> but a IService<WorldRegion> is not a IWorldRegionService.
You don't want to register a generic service but want to register all children as a IService<T>, you can do this by using the RegisterAssemblyTypes method with the AsClosedTypedOf
builder.RegisterAssemblyTypes(this.GetAssemblies())
.AsClosedTypesOf(typeof(IService<>));
Read Autofac documentation - Assembly scanning for more information and how to properly implement the GetAssemblies method in IIS.
At first, I tried removing the abstract from Service<T>, that didn't work, same error. Then I read the Autofac docs and searched around, and I found something similar to Cyril's answer that works:
builder.RegisterAssemblyTypes(typeof(Service<>).Assembly)
.Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces();
I tried Cyril's implementation of builder.RegisterAssemblyTypes() but this.GetAssemblies() wasn't available. I had to use the full path and this also works:
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.AsClosedTypesOf(typeof(IService<>));
To note, using either builder.RegisterTypes(), the Service<T> can still be abstract. And just to test, I changed WorldRegionService to abstract, and that DID NOT work.
// THIS DOES NOT WORK..!!!! Must remove 'abstract' from this class.
public abstract class WorldRegionService {}
So both above builder.RegisterTypes() work.
I am hoping someone can tell me what is wrong with my implementation of Castle Windsor. I have followed the tutorial Here. I have added a second installer that looks like this:
public class DataInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
//breakpoint on the line below is hit when the application starts.
container.Register(Component.For<IDataProvider>().ImplementedBy<MyDataProvider>());
}
}
My controller looks like this:
public IDataProvider Provider { get; set; }
public JsonResult Get()
{
//Provider is always null!!!
var data = Provider.Retrieve(a => true).ToArray();
//convert to JSON and return.
}
Why is my provider always null? My second installer is being hit when the application fires up, because it hits a breakpoint, but from what I can tell the rest of the application just ignores this.
Contents of my global.asax.cs
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
BootstrapContainer();
}
private static void BootstrapContainer()
{
container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
//container.Register(Component.For<IDataProvider>().ImplementedBy<MyDataProvider>().LifestyleSingleton());
}
I tried what the person did in this question, but that still resulted in the same problem. What am I missing here?
Edit: Someone asked if I registered the controller. The tutorial had me create the following class. I believe this would be registering the controllers?
public class ControllersInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
}
}
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