ASP.NET MVC: HTTPContext and Dependency Injection
Asked Answered
P

4

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.

Penetrant answered 18/5, 2009 at 13:20 Comment(0)
F
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.

Footloose answered 18/5, 2009 at 16:34 Comment(4)
What do you mean, "Add generic type params to bags"? Sounds intriguing.Nicely
provide strong typed wrappers over SessionFootloose
do you have any sample code/ links, this seems interesting _ am kinda new to DI so I dont understand this properly, any help/advice would be appreciated...Concubine
Can you give an example of this, please?Severson
B
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));
Bromo answered 8/2, 2013 at 14:10 Comment(0)
F
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.

Footloose answered 18/5, 2009 at 16:34 Comment(4)
What do you mean, "Add generic type params to bags"? Sounds intriguing.Nicely
provide strong typed wrappers over SessionFootloose
do you have any sample code/ links, this seems interesting _ am kinda new to DI so I dont understand this properly, any help/advice would be appreciated...Concubine
Can you give an example of this, please?Severson
C
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...

Conlin answered 18/5, 2009 at 15:42 Comment(3)
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?Penetrant
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.Penetrant
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)...Concubine
P
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 :*( ).

Penetrant answered 18/5, 2009 at 14:44 Comment(3)
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.Entwistle
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.Penetrant
indeed my solution did not work - and with a hardcore error even, the inner exception said something about a JIT limitation ... wowSuccessive

© 2022 - 2024 — McMap. All rights reserved.