1
votes

I am trying to solve this case in our code where I need to resolve the dependency at runtime based on a specific condition, such as if certain query string value exist or not.

Let say I have a controller AuthenticationController and I have authentication service having two flavours of it.

public class AuthenticationController
{
    private readonly IAuthenticationService authenticationService;

    public AuthenticationController(IAuthenticationService authenticationService)
    {
        this.authenticationService = authenticationService;
    }

    public ActionResult LogOn(LogOnModel model)
    {
        var isAuthenticated = authenticationService.AuthenticatUser(model.UserName, model.Password);
    }
}

public interface IAuthenticationService
{
    bool AuthenticatUser(string userName, string password);
}

public class ProviderBasedAuthenticationService : IAuthenticationService
{
    public bool AuthenticatUser(string userName, string password)
    {
        // Authentication using provider;
        return true;
    }
}


public class ThirdPartyAuthenticationService : IAuthenticationService
{
    public bool AuthenticatUser(string userName, string password)
    {
        // Authentication using third party;
        return true;
    }
}

I have implemented IoC and DI using castle windsor.

I am registering both ProviderBasedAuthenticationService and ThirdPartyAuthenticationService for IAuthenticationService in Castle container.

Currently Castle resolves object of the first registered type when resolving IAuthenticationService.

I wish to resolve appropriate type of IAuthenticationService depending on certain value coming as part of query string in the request URL or route data.

I found that this can be somehow done using Microsoft UnityContainer but I am not sure how to achieve this in Castle Windsor. (I can not change my container now to Microsoft UnityContainer).

It would be much appreciated if anyone can help me on this or provide some direction around this.

Thanks and regards, Chetan Ranpariya

1

1 Answers

6
votes

You can do this with a factory method. Here's an example:

private WindsorContainer ContainerFactory()
{
    var container = new WindsorContainer();
    container.Register(
        Component.For<ProviderBasedAuthenticationService>()
            .LifeStyle.Transient,
        Component.For<ThirdPartyAuthenticationService>()
            .LifeStyle.Transient,
        Component.For<AuthenticationController>()
            .LifeStyle.Transient,
        Component.For<IAuthenticationService>()
            .UsingFactoryMethod((k, c) => this.AuthenticationServiceFactory(k)));

    return container;
}

private IAuthenticationService AuthenticationServiceFactory(IKernel kernel)
{
    return HttpContext.Current != null &&
           HttpContext.Current.Request != null &&
           HttpContext.Current.Request.QueryString["SomeKey"] != null
        ? (IAuthenticationService)kernel.Resolve<ThirdPartyAuthenticationService>()
        : (IAuthenticationService)kernel.Resolve<ProviderBasedAuthenticationService>();
}

And 2 unit tests to prove the solution

[Fact]
public void Resolve_WithoutTheRequiredQueryString_ReturnsProviderBasedAuthenticationService()
{
    var container = this.ContainerFactory();

    var result = container.Resolve<AuthenticationController>();

    Assert.IsType<ProviderBasedAuthenticationService>(result.authenticationService);
}

[Fact]
public void Resolve_WithTheRequiredQueryString_ReturnsThirdPartyAuthenticationService()
{
    var container = this.ContainerFactory();
    HttpContext.Current = new HttpContext(
        new HttpRequest("", "http://localhost", "SomeKey=Value"),
        new HttpResponse(null));

    var result = container.Resolve<AuthenticationController>();

    Assert.IsType<ThirdPartyAuthenticationService>(result.authenticationService);
}