Search code examples
c#asp.net-core-webapiinversion-of-controlcastle-windsor

How to resolve Windsor container dependencies for a class library in ASP.NET Core Web API project?


I have a ASP.NET Core Web API, whose Program.cs looks like below,

using Castle.Windsor;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();

var container = new WindsorContainer();

container.Install(new MyInstaller());

var adapter = container.Resolve<IMyApiAdapter>();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

I am referencing my class library project in ASP.NET Core Web API, My Installer class from the class library looks like this,

    public class MyInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Component.For<IDbConnectionFactory>()
                .ImplementedBy<SqlDbConnectionFactory>()
                .DependsOn(Dependency.OnValue("connectionString", container.Resolve<IBaseDAL>().ConnectionString))
                .LifestyleSingleton());

            container.Register(Component.For<IClientResponseAdapter>()
                .ImplementedBy<ClientResponseAdapter>().LifestyleTransient());

            container.Register(Component.For<IMyApiAdapter>()
                .ImplementedBy<MyApiAdapter>()
                .DependsOn(Dependency.OnValue("container", container))
                .LifestyleTransient());
        }
    }

Once I run my Web API, I get the below error,

enter image description here

How to resolve this error?

And once resolved, I need to use MyApiAdapter class methods in the Controller. How should I reference my dependencies in the Controller class?

Controller class

[ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {


private IMyApiAdapter _adapter;


        [HttpGet(Name = "GetWeatherForecast")]
        public string GetAsync()
        {
            HelperResponse hr = new HelperResponse();
            hr = _adapter.SetFormActiveFlag(100, true, "","");
            return hr.ResponseXml;
        }
}

UPDATE after implementing the answer:

Do I need to do something separately to install IMyApiAdapter related dependencies?

In the old implementation, I see in the constructor it is being resolved.

public class MyHandler: BaseHandler
    {
        #region Private Members
        private IDynamicFormsApiAdapter _adapter;

        #endregion Private Members

        #region Constructors
        public MyHandler()
        {
            _adapter = WebApplicationBase.Container.Resolve<IMyApiAdapter>();
        }
        #endregion Constructors
}

And WebApplicationBase.cs looks like this,


public class WebApplicationBase : System.Web.HttpApplication
    {
        protected static IIocContainer _iocContainer = new IocContainer();

        protected virtual void SetupWindsorContainer(params Castle.MicroKernel.Registration.IWindsorInstaller[] installers)
        {
            _iocContainer.RegisterInstaller(installers);
        }

        public static IWindsorContainer Container
        {
            get
            {
                return _iocContainer.Container;
            }
        }
    }

enter image description here enter image description here


Solution

  • The error you experiencing is due to the fact that there is no IBaseDAL implementation registered in the container when you are registering the IDbConnectionFactory. You should register the IBaseDAL implementation before your connection factory.

    container.Register(Component.For<IBaseDAL>().ImplementedBy<YourDALImpl>());
    

    If your IBaseDAL isn't registered until later (some other Windsor installer) then you can use DynamicParameters to provide that dependency and it will be resolved when the factory is instantiated.

    container.Register(Component.For<IDbConnectionFactory>()
        .ImplementedBy<SqlDbConnectionFactory>()
        .LifestyleSingleton()
        .DynamicParameters((k, d) => d["connectionString"] = k.Resolve<IBaseDAL>().ConnectionString));
    

    Now that the error is resolved we can address how to get that dependency into your controllers.

    The first thing you want to do is integrate the IWindsorContainer as the IServiceProvider for your Web API project. This needs to be done before you build the WebApplication.

    //...
    builder.Services.AddSwaggerGen();
    
    // Add services via windsor
    var container = new WindsorContainer();
    builder.Host.UseWindsorContainerServiceProvider(container);
    container.Install(new MyInstaller());
    
    var app = builder.Build();
    //...
    

    UseWindsorContainerServiceProvider is an extension method provided from the NuGet package Castle.Windsor.Extensions.DependencyInjection.

    Since the IWindsorContainer is now our service provider, anything registered to it can be injected into controllers either through "constructor injection" or through "method injection".

    Constructor injection uses the constructor to acquire dependencies. Your weather controller would look like this:

    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly IMyApiAdapter _adapter;
    
        public WeatherForecastController(IMyApiAdapter adapter)
        {
            _adapter = adapter;
        }
    
        [HttpGet(Name = "GetWeatherForecast")]
        public string GetAsync()
        {
            HelperResponse hr = new HelperResponse();
            hr = _adapter.SetFormActiveFlag(100, true, "", "");
            return hr.ResponseXml;
        }
    }
    

    You can also request dependencies on the controller action methods using the FromServices attribute:

    [HttpGet(Name = "GetWeatherForecast")]
    public string GetAsync([FromServices] IMyApiAdapter adapter)
    {
        HelperResponse hr = new HelperResponse();
        hr = adapter.SetFormActiveFlag(100, true, "", "");
        return hr.ResponseXml;
    }
    

    You can read all about DI into ASP .NET Core controllers here.