18

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.

anonymous
  • 6,705
  • 8
  • 44
  • 59

4 Answers4

10

It sounds like you should be using HttpContextBase instead of HttpContextUserProvider. This is a out-of-box abstraction of HttpContext and allows you to create a mock, write UnitTests and inject your dependencies.

public class SomethingWithDependenciesOnContext
{
    public SomethingWithDependenciesOnContext(HttpContextBase context) {
        ...
    }

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

ObjectFactory.Initialize(x => 
          x.For<HttpContextBase>()
          .HybridHttpOrThreadLocalScoped()
          .Use(() => new HttpContextWrapper(HttpContext.Current));
TheCodeKing
  • 18,516
  • 2
  • 45
  • 69
8

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.

CarenRose
  • 1,142
  • 11
  • 18
Matt Hinze
  • 13,348
  • 3
  • 31
  • 40
4

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
  • 61,606
  • 8
  • 70
  • 82
  • 2
    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? – anonymous May 18 '09 at 15:45
  • 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. – anonymous May 18 '09 at 15:58
  • 1
    How would you inject the HttpContext in say a service layer that does not reference the web, maybe we could say this is a use case scenario for that, where records/ data set depend on the current user? (asp.net mvc2)... – Haroon Jun 23 '11 at 05:33
2

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: If you need something in StructureMap, but you can’t build it with new()…


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 :*( ).

CarenRose
  • 1,142
  • 11
  • 18
anonymous
  • 6,705
  • 8
  • 44
  • 59
  • 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 May 18 '09 at 15:23
  • 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. – anonymous May 18 '09 at 15:38
  • indeed my solution did not work - and with a hardcore error even, the inner exception said something about a JIT limitation ... wow – sirrocco May 18 '09 at 16:05