views:

1472

answers:

3

Hello,

Currently I have an ActionFilter that gets the current users name from HttpContext and passes it into the action which uses it on a service method. eg:

Service.DoSomething(userName);

I now have a reason to do this not at the action level but the controller constructor level. Currently I'm using structure map to create controllers and inject the service. I'm looking at something like:

public interface IUserProvider
{
    string UserName { get; }
}

public class HttpContextUserProvider : IUserProvider
{
    private HttpContext context;

    public HttpContextUserProvider(HttpContext context)
    {
        this.context = context;
    }

    public string UserName
    {
        get
        {
            return context.User.Identity.Name;
        }
    }
}

That said, my IoC foo is really weak as this is the first project I've used it on.

So my question is... how can I tell structure map to pass in HttpContext in the constructor for HttpContextUserProvider? This just seems weird... I'm not sure how to think of HttpContext.

+2  A: 

Maybe I left something out, but the above answer doesn't work for me (has since been deleted -- it was still a useful answer though -- it showed how to tell SM to pass constructor arguments). Instead if I do:

ObjectFactory.Initialize(x =>
{
    x.BuildInstancesOf<HttpContext>()
         .TheDefault.Is.ConstructedBy(() => HttpContext.Current);
    x.ForRequestedType<IUserProvider>()
         .TheDefault.Is.OfConcreteType<HttpContextUserProvider>();
});

I get it to work. I did this after finding: http://codebetter.com/blogs/jeremy.miller/archive/2008/03/20/if-you-need-something-in-structuremap-but-you-can-t-build-it-with-new.aspx


edit:

Thanks to Brad's answer I think I have a better handle on HttpContext. His answer definitely works, I just am not sure I like having the call to HttpContext.Current inside a class (it seems like it hides the dependency, but I'm far from an expert on this stuff).

The above code should work for injecting HttpContext as far as I can tell. Matt Hinze brings up the added that point that if all I need from HttpContext is the User.Identity.Name, my design should be explicit about that (having an Interface around HttpContext only exposing what I need). I think this is a good idea.

The thing is over lunch I kinda realized my service really only needs to depend on a string: userName. Having it depend on IUserProvider might not have much added value. So I know I don't want it to depend on HttpContext, and I do know all I need is a string (userName) -- I need to see if I can learn enough StructureMap foo to have make this connection for me. (sirrocoo's answer gives a hint on where to start but he deleted it :*( ).

eyston
In laymens terms when a constructor has a dependency of HttpContext the IoC will pass in the instance of HttpContext.Current. And when a constructor has a dependency of IUserProvider the IoC will instantiate a new instance of HttpContextUserProvider and pass that into the constructor.
Todd Smith
The StructureMap interface reads well. My confusion is I think in two parts: 1) I've only ever used ForRequestedType -- how do I know when to break out BuildInstancesOf instead (I can probably just google this -- easy question) 2) Can I just take for granted that StructureMap knows how to get HttpContext.Current? I guess HttpContext confuses me a bit in that it just seems to magically float around everywhere. I can't think of a single source I can go to to get it, where as everything else has a more clear source.
eyston
indeed my solution did not work - and with a hardcore error even, the inner exception said something about a JIT limitation ... wow
sirrocco
A: 

I'm not sure why you're bothering. It seems like just using HttpContext.Current directly in HttpContextUserProvider is the right thing to do. You're never going to be substituting in a different HttpContext...

Brad Wilson
Yah... I don't think I *get* HttpContext. I guess I'm used to having something passed in via constructor where as HttpContext appears to just globally be there?
eyston
Okay, made the return HttpContext.Current.User.Identity.Name; and it works. I guess I'm kinda slow on the uptake here but Current a static property on HttpContext that has the responsibility of knowing how to find the current HttpContext. The only downside is you can't (I don't think at least) be explicit about depending on HttpContext via constructor injection because you can't pass in a static property or type? You could make some wrapper like IHttpContextProvider that just returns HttpContext.Current and then you'd know something depends on HttpContext? Or is that dumb.
eyston
Maybe he's bothering because he wants to be able to unit test a critical security feature of his app. You MS guys.. sheesh. =)
Matt Hinze
+2  A: 

Have an interface abstract HttpContext.Current. Expose only the methods you need. GetUserName() would call HttpContext.Current.User.Identity.Name in the implementation, for example. Make that as thin as possible.

Take that abstraction and inject it into your other provider class. This will allow you to test the provider by mocking the http context abstraction. As a side benefit, you can do other nifty things with that HttpContext abstraction besides mock it. Reuse it, for one thing. Add generic type params to bags, etc.

Matt Hinze
What do you mean, "Add generic type params to bags"? Sounds intriguing.
Charlie Flowers
provide strong typed wrappers over Session
Matt Hinze