1

I have an http client wrapper that I'm injecting into all my controllers. If a user is authenticated, the injected wrapper should have some properties set with the authenticated user information.

I currently have this:

[System.Web.Mvc.Authorize]
public class ProfileController : Controller
{
    private readonly IMyClient client;

    public ProfileController()
    {
        string apiKey = ConfigurationManager.AppSettings["ApiKey"];
        client = new MyClient(apiKey);

        SetupClient();
    }

    private void SetupClient()
    {
        if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
        {
            var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
            var tokenClaim = identity.Claims.First(c => c.Type == ClaimTypes.Sid);
            client.AddCredentials(tokenClaim.Value);
        }
    }
}

I would like to offload SetupClient to somewhere that will allow me to do dependency injection of IMyClient.

Essentially I want to implement this solution:

ProfileController.cs

[System.Web.Mvc.Authorize]
public class ProfileController : Controller
{
    private readonly IMyClient client;

    public ProfileController(IMyClient client)
    {
        this.client = client;
    }
}

Startup.cs

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        IoCConfig.RegisterIoC(app);
        ConfigureAuth(app);
    }
}

IoCConfig.cs

public class IoCConfig
{
    public static void RegisterIoC(IAppBuilder app)
    {
        var container = new Container();
        container.Register<IMyClient>(
            () =>
            {
                var apiKey = ConfigurationManager.AppSettings["APIKey"];
                var myClient= new MyClient(apiKey);

                // This will not work as this code is executed on app start
                // The identity will not be of the user making the web request
                var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
                var tokenClaim = identity.Claims.First(c => c.Type == ClaimTypes.Sid);
                client.AddCredentials(tokenClaim.Value);

                return myClient;
            });
        // Register the dependency resolver.
        DependencyResolver.SetResolver(
            new SimpleInjectorDependencyResolver(container));
    }
}

I'm stuck in the code for IoCConfig to extract information of the authenticated user (if the user is authenticated) and setup the client for injection. Any help here?

My IoC framework is SimpleInjector but I'd like an agnostic solution.

3 Answers 3

1

This is how I would do it

public class ProfileController : Controller
{
    private readonly MyClient _client;

    public ProfileController()
    {
        var clientInfo = Resolve<IClientInfo>(); // call out to your service locator
        _client = clientInfo.GetClient();
    }
}

public interface IClientInfo
{
    MyClient GetClient();
}

public interface IAuth
{
    System.Security.Claim GetSidClaim();
}

public class ClientInfo : IClientInfo
{
    private readonly IAuth _auth;

    public ClientInfo(IAuth auth)
    {
        _auth = auth;
    }

    public MyClient GetClient()
    {
        var apiKey = ApiKey;
        var client = new MyClient(apiKey);
        var claim = _auth.GetSidClaim();
        client.AddCredentials(claim.Value);

        return client;
    }

    protected virtual string ApiKey
    {
        get { return ConfigurationManager.AppSettings["APIKey"]; }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

I was thinking about a lazy factory solution similar to this. I think using a factory delegate might be the way to go: simpleinjector.codeplex.com/…
This way it's up to ClientInfo to figure out how to make a MyClient. I would improve it futher to break the appsettings and maybe the identity dependencies, so there would be another interface or two and their instances would be injected into the ClientInfo constructor, or if its a once off you could return those from a protected virtual method to allow the dependencies to be substituted
Could you edit your answer to show those additional changes you mentioned?
Ok, well it's just an example anyway, up to you how the design goes.
1

I'd take a look at NInject and the MVC extensions...

http://ninject.codeplex.com/wikipage?title=Dependency%20Injection%20With%20Ninject http://www.codeproject.com/Articles/412383/Dependency-Injection-in-asp-net-mvc-and-webapi-us

When setup correctly it's just a matter of creating a binding for IMyClient NInject will implicitly inject it for you. There are lots of other injection frameworks out there, NInject is just the one I've chosen. Each of them will give you a substantial benefit over anything you could cook up on your own. e.g. with NInject you can create bindings that inject a singleton across your app or a binding that injects a singleton for each request.

In NInject you could create a binding something like

Bind<IMyClient>().ToMethod(x => SetupClient(x)).InRequestScope();

private IMyClient SetupClient(IContext context)
{
    string apiKey = ConfigurationManager.AppSettings["ApiKey"];
    var client = new MyClient(apiKey);
    if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
    {
        var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
        var tokenClaim = identity.Claims.First(c => c.Type == ClaimTypes.Sid);
        client.AddCredentials(tokenClaim.Value);
    }
    return client;
}

InRequestScope says that NInject should create a single instance for each request...

https://github.com/ninject/Ninject.Web.Common/wiki/InRequestScope

I think the equivalent in SimpleInjector is...

https://simpleinjector.codeplex.com/wikipage?title=ObjectLifestyleManagement#PerWebRequest

Is the answer as simple as changing your code to...

public static void RegisterIoC(IAppBuilder app)
{
    var container = new Container();
    container.RegisterPerWebRequest<IMyClient>(
        () =>
        {
            ...

8 Comments

Ninject has many great features and covers a lot of integration scenarios, but unfortunately it is really slow as demonstrated in this article (and validated in my own tests).
Really slow? How many projects have you worked on that required you to inject half a million objects? Sorry but it's a little ridiculous, I think typical use of NInject would for a single request involve maybe at absolute most a couple hundred injections, going by your own figures that would mean it would take up 2 millisecs of the request, compared to the typical database calls which would total in the hundreds of millisecs. It's this kind of logic that drives people to abandon OOP all together in favour of using static methods everywhere. False economies. It's powerful, use it!
Having said that I did say NInject is just the framework I chose, feel free to recommend another. If you're going to do IOC I think it's more important to choose a IOC Framework, than not to choose any at all and reinvent wheels
I didn't say to abandon IOC or OOP. I was a big fan of Ninject until browsing its source and noticing its locking implementation (which is quite slow). That led me to research (I found the article I linked) and my own tests. It's literally 1000x slower than my current IOC container (SimpleInjector) and seems to slow down further under concurrent load. You shouldn't have to worry about something so basic as object instantiation.
If it works for you, great. I just point it out because I spent/wasted time on it and there are alternatives that will do exactly the same thing. All things being equal, I will recommend/take the faster route even if it isn't performance critical (and creating a few million objects isn't that hard to do in a production app).
|
0

I solved this by a version of what CRice posted by using a factory delegate:

ProfileController.cs

[System.Web.Mvc.Authorize]
public class ProfileController : Controller
{
    private readonly IMyClient client;

    public ProfileController(Func<IMyClient> clientFactory)
    {
        client = clientFactory.Invoke();
    }

}

IoCConfig.cs

public class IoCConfig
{
    public static void RegisterIoC(IAppBuilder app)
    {
        // Create the container as usual.
        Container container = new Container();

        // Registering as a factory delegate because we need the user authentication information if any.
        container.RegisterSingle<Func<IMyClient>>(() =>
        {

            string apiKey = ConfigurationManager.AppSettings["ApiKey"];
            var myClient = new MyClient(apiKey);
            if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
            {
                var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
                var tokenClaim = identity.Claims.First(c => c.Type == ClaimTypes.Sid);
                myClient.AddCredentials(tokenClaim.Value);
            }
            return myClient;
        });


        // This is an extension method from the integration package.
        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

        // This is an extension method from the integration package as well.
        container.RegisterMvcIntegratedFilterProvider();

        container.Verify();
        DependencyResolver.SetResolver(
    new SimpleInjectorDependencyResolver(container));
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.