Rabid Penguin
Rabid Penguin

Reputation: 1104

How to resolve instances with Castle Windsor that rely on values from the HttpRequestMessage

I'm using Web Api with the OWIN pipeline.

Startup.cs

public class Startup {
    public void Configuration(IAppBuilder app) {
        var container = new WindsorContainer().Install(FromAssembly.This());

        var config = new HttpConfiguration();
        config.MapHttpAttributeRoutes();

        // extension method to resolve controllers with Windsor.
        app.UseWindsorApi(config, container);
    }
}

MyClassInstaller.cs (IWindsorInstaller)

public void Install(IWindsorContainer container, IConfigurationStore store) 
{
    container.Register(
        Component.For<IMyClass>().ImplementedBy<MyClass>()
            .LifeStyle.PerWebRequest()
            .DependsOn(new {
                configSetting = ConfigurationManager.AppSettings["key"],
                otherSetting = ???
            }));
}

MyClass.cs & IMyClass.cs

public class MyClass : IMyClass {
    private readonly string configSetting;
    private readonly string otherSetting;

    public MyClass(string configSetting, string otherSetting) {
        this.configSetting = configSetting;
        this.otherSetting = otherSetting;
    }

    public void DoSomething() {
        // method that uses the settings that were set in the constructor.
    }
}

public interface IMyClass {
    void DoSomething();
}

MyController.cs

public class MyController : ApiController {
    private readonly IMyClass myClass;

    public MyController(IMyClass myClass) {
        this.myClass = myClass;
    }

    [HttpGet]
    [Route("")]
    public async Task<IHttpActionResult> GetAsync() {
        // uses this.myClass
    }
}

Here's where I'm stuck. Whenever an instance of MyClass is resolved the value of otherSetting needs to be assigned.

The value of otherSetting is determined by two things.
1) The client_id claims value from the request.
2) An async call to a method that takes the client_id as a parameter and returns a string value. Which is what gets set into otherSetting

public async Task<string> GetOtherSetting(string client_id) {
    return "value";
}

I'm not sure where to even begin to get Castle to inject a value based on those two criteria...

UPDATE: I've updated to potatopeelings answer with some minor changes and it seems to be working fine.

.DynamicParameters(async (k, p) =>
{
    var fundNameProvider = k.Resolve<IFundNameValueProvider>();
    p["otherSetting"] = await fundNameProvider.GetFundNameAsync();
    k.ReleaseComponent(fundNameProvider);
}))

I changed it to an async lambda so I can await the method. I also called ReleaseComponent as I was under the impression that objects you manually Resolved with Castle you also needed to manually release.

Upvotes: 0

Views: 925

Answers (1)

potatopeelings
potatopeelings

Reputation: 41065

Use UsingFactoryMethod and DynamicParamters

First, inject the current claims

...
Component.For<ClaimsIdentity>().UsingFactoryMethod(() => HttpContext.Current.User.Identity as ClaimsIdentity).LifestylePerWebRequest()
...

into a service (IOtherValueProvider - PerWebRequest) that has a GetOtherSetting method to wait on an async call (i.e. convert the async call to a synchronous call) to get otherSetting from the client_id extracted from the injected ClaimsIdentity

Then use DynamicParameters to get the value

... register your class ...
.DynamicParameters((kernel, parameters) =>
    {
        parameters["otherSetting"] = kernel.Resolve<IOtherValueProvider>().GetOtherSetting();
    }))

Upvotes: 1

Related Questions