HttpContextWrapper and HttpContextBase, as explained here, were introduced to make HttpContext more mockable/testable.
I'm trying to use it with S#arp Architecture, and hitting some problems.
My MVC Controllers are set up to accept an HttpContextBase argument in the constructor, and during Application_Start, HttpContextBase is registered with Castle.Windor as follows:
container.Register(Component.For<HttpContextBase>().UsingFactoryMethod(
() => new HttpContextWrapper(HttpContext.Current)));
This seemed to work OK for a bit, but then I realised Castle is only running that Factory method once, so all requests get the original HttpContextWrapper. Really it needs to be re-created for every request. The Castle.Windsor command for that would be:
container.Register(Component.For<HttpContextBase().
LifeStyle.PerWebRequest.UsingFactoryMethod(
() => new HttpContextWrapper(HttpContext.Current)));
... but it turns out that Castle.Windsor doesn't allow LifeStyle.PerWebRequest to be used within Application_Start (as explained here)
What should I be doing? Is there an easy way round this or should I give up on HttpContextWrapper and inject my own factory to make new ones as needed?
My MVC Controllers are set up to accept an HttpContextBase argument in the constructor
You gotta be doing something extremely wrong here, so stop before it's too late and damage has been caused (material, moral and human casualties :-)). You already have the HttpContext inside the controller.
Don't register any HttpContexts in your DI framework. The HttpContext handling is the job of ASP.NET.
As Darin noted, it makes no sense to inject an HttpContext into an MVC controller. However, if you need it for other kind of services and also need it in Application_Start(), use an hybrid perwebrequest-transient lifestyle. Or, since it's trivial to build, just make it transient.
As others have stated - you are doing it wrong. My big question is:
What are you doing that requires you to inject HttpContextBase in your controller? It might be more helpful to people wanting to help you if you would provide us more context about what you are really trying to do. Lets take Castle out of it and get down to what your controller is doing.
BTW, your controller already has a reference to HttpContext. If you are doing this for testability, there is nothing you need to do at the controller level. You would just need to mock the HttpContextBase object as needed in your controller tests.
Related
I like to have static helper classes in my apps for common plumbing stuff, like checking roles, or Razor Html helpers and stuff... but how do you do this in the DI world?
Like lets say I want an extension helper to check if a user is an admin
public static async Task<bool> IsAdmin(this ApplicationUser user)
...
if(user.IsAdmin()){...}
So pre-core I could just ignore DI and create a UserManager all inside IsAdmin do whatever I need to do. But now is there no way to get the UserManager in these helpers to just use? The only way I can see is to inject it into the Controller, then pass along into the method (which I find ugly). Then there's the issue of trying to do user.IsAdmin() in the Razor view, would I need to add the UserManager to the ViewData collection to get it into the view markup?
Am I just missing something here?
Thanks,
Steve
First of all if you just asked how to use static class with di, i would say that your question is duplicate of How to use DI inside a static Method in Asp.net Core rc1
But as i see, you have some other question?
But now is there no way to get the UserManager in these helpers to
just use?
Yes there is a way : Service Locator pattern. But it is an anti pattern (see this article) . As far as possible you should avoid to use this pattern. Also see discussion in github.
The only way I can see is to inject it into the Controller, then pass
along into the method (which I find ugly)
I think this way is better than you want. I would prefer this.
Then there's the issue of trying to do user.IsAdmin() in the Razor
view, would I need to add the UserManager to the ViewData collection
to get it into the view markup?
In Aspnet core you can inject a dependency into a view, so you don't need to use ViewData. Simply you can inject UserManager into your view and then pass it as parameter to method. Take a look at official docs
Well you are talking about a cross-cutting concern here and one way how I've seen cross cutting concerns solved in ASP.NET Core MVC is with attributes (like [Authorize] for example). Which I think is an elegant solution.
So, if I understand your question correctly I think you can solve this with an Action Filter. Damien Bod described a few days ago how to use ActionFilters: https://damienbod.com/2016/09/09/asp-net-core-action-arguments-validation-using-an-actionfilter/.
So in short, you inherit from ActionFilterAttribute and make your own curstom filter called MyCustomFilter or whatever. Have this MyCustomFilter request UserManager in its constructor via DI. Then above any action method in a controller you say:
ServiceFilter[typeof(MyCustomFilter)]
And in MyCustomFilter you ofcourse have logic to check if User is IsAdmin and then take action accordingly.
Now, I've always used Microsoft's Unity to handle cross cutting concerns via interception (you can read more about that here: https://dannyvanderkraan.wordpress.com/2015/09/30/real-world-example-of-adding-auditing-with-dependency-injections-interception/. But last time I checked there is no Unity container for asp.net core yet. But this guy has a great article about porting it to core: https://dzimchuk.net/post/bring-your-own-di-container-to-aspnet-5-unity. I would really like my Interception back! Very elegant solution to cross cutting concerns. They are working on it though: https://github.com/unitycontainer/unity/issues/66. Fingers crossed...
ASP.NET MVC has come a long way (compared to webforms) in becoming a unit testable framework. However, we are often faced with some remaining pigs like FormsAuthentication, which I usually wrap in some type of UserSession object to keep it clean and testable. The other day I realized I was using Server.MapPath in my controller action, and while things like MvcContrib make it easy(ish) to mock the current request, I found myself going down the path of creating a FileSystemService class to wrap operations with the file system. The benefits are that you get a tight API exposing just the methods you need, and it's easy to stub/mock in tests. The downside is that it is yet another constructor dependency.
What does the SO community thing about this situation? Where do you draw the line when trying to make your controllers as light and flexible as possible?
I would generally draw the line at the point where you need to actually get something done (i.e. somebody wants to use the code you're writing). If it takes another half a day to write your service etc. and your deadline is looming, then some things are best left...
Bear in mind that ASP.NET MVC has already replaced the sealed HttpContext with more test-friendly HttpContextBase, which you can mock to provide your tests with your chosen implementations of Server.*, Request.*, Response.*, etc.
You just need to set the controller's ControllerContext to provide your test context:
controllerUnderTest.ControllerContext = new ControllerContext( testHttpContext, new RouteData(), controllerUnderTest );
This is a great question! I do the same thing you do - wrap HttpContext dependencies so they can be visible, inverted, and mocked.
I draw the controller line at validating the post values and saving or passing the call off to a task layer. So if I have to validate, save some data, send an email to marketing if a preferred customer makes a purchase, log something, etc., that's too much. I'd put that in a task layer and have my controller call that task layer. Most of the dependencies then float to the task layer constructors and come out of the controller.
My general rule of thumb is this: If you were asked to create a Silverlight or some other UI on your app, would you be totally rewriting it? Controllers should stick to presentation logic. If you can scrape off the presentation layer and put another one on, I think that gives you the best future proofing.
Of course, where you draw the line is up to you and your app. See Who Can Help Me, which is based on Sharp Architecture, for more about the task layer and skinny controllers.
There are also these wrappers around things like HttpContext to make testing easier:
http://www.codethinked.com/post/2008/12/04/Using-SystemWebAbstractions-in-Your-WebForms-Apps.aspx
So to sum it up:
Get System.Web.Abstractions
Use HttpContext base as a parameter, not HttpContext:
public void SomeMethod(HttpContextBase context)
{
}
Call the method wrapping your real http context
HttpContextBase contextBase = new HttpContextWrapper(context);
SomeMethod(contextBase);
You can provide fakes like so (example using Moq):
var context = new Mock<HttpContextBase>();
context.Setup(x => x.Request.QueryString).Returns(new NameValueCollection { { "Id", "5" } });
I want to test a mvc controller. I'm using Moq to mock the services, but I don't know how to mock this.Request.Files["Attachement1"] and this.Server.MapPath("~/Temp") ("this" is the controller)
I tried to create a new Mock<HttpRequestBase>(); but this.Request doesn't have a setter.
Help me please with an advise. Thanks
I had a similar problem as your - I used the set of fake classes from Stephen Walther's blog.
Asp.Net MVC Tips - Faking the Controller Context
I had to modify some of the classes slightly but it should do what you want and it's definitely a lot easier to setup than having to mock the entire context every time.
For the folder name resolutions such as: this.Server.MapPath("~/Temp") I use public properties, and getter returns this so I can easily test it.
For the Request.Files, I prefer using FormCollection dictionary
If you have a look at the TestHelper in the MVCContrib project, it can easily be extended to Mock other bits of the Http elements. (It has some Request elements already to use as a template.)
Kindness,
Dan
I really love the "one model in - one model out" idea of Fubu MVC. A controller would look something like this
public class MyController
{
public OutputModel MyAction(InputModel inputModel)
{
//..
}
}
and the service locator would automagically fill in all the required dependencies in the constructor.
This makes the controller very easy to test.
So my question is: How would you go about tweaking asp.net mvc to allow this simplicity in the controllers ?
What you're looking for the is the ControllerActionInvoker. You'll have to implement your own and override/take over handling a lot of the pipeline work that ASP.NET MVC.
For reference, check out Jonathon Carter's 2-part post on doing ControllerActionInvokers:
http://lostintangent.com/2008/07/03/aspnet-mvc-controlleractioninvoker-part-1/
and
http://lostintangent.com/2008/07/07/aspnet-mvc-controlleractioninvoker-part-2/
Also, the Oxite team did this in the 2nd release of Oxite, you can check out their source here:
http://oxite.codeplex.com/SourceControl/changeset/view/30544
Here's a link directly to their ControllerActionInvoker implementation:
http://oxite.codeplex.com/SourceControl/changeset/view/30544#442766
Never really dug deep inside ASP.NET MVC internals, but I guess custom ModelBinder and ActionResult will do the job.
I have something similar to the following method:
public ActionResult Details(int id)
{
var viewData = new DetailsViewData
{
Booth = BoothRepository.Find(id),
Category = ItemType.HotBuy
};
return View(viewData);
}
and the following Route:
routes.MapRoute("shows","shows/{controller}/{action}/{id}", new {id = 0});
Everything worked fine before the Beta, when I had Preview 3. Now the method will fill the id correctly the first time I execute the action. However the second time the controller's ModelState contains the last-use id value. This causes the ActionInvoker to use it in the method's parameter instead of the Route value.
So if I call the action twice on two different entities the results are such:
www.mysite.com/shows/Booth/Details/1 => Details(1)
www.mysite.com/shows/Booth/Details/2 => Details(1) //from ModelState["id"]
From my quick scan with Reflector it seems it first binds parameters to the ModelState then to Routes. However, I never even posted anything from the model. As far as I can tell the ModelState should not contain anything.
Is this a bug in the Beta, possibly a bug somewhere in my code, or is there some design feature that I am ignorant of? Any insight into the nature of ModelState and why this happens is appreciated.
EDIT:
I discovered that this issue is actually a symptom of what appears to be a bug with the DefaultValueProvider if you instantiate a Controller from an IoC container that exists for the lifetime of the Asp.Net application.What happens is that the DefaultValueProvider uses the first ControllerContext given to the Controller and never updates it until the controller is recreated. This causes old RouteData to be used for method parameters instead of the current RouteData.
It's hard for me to tell what you expect to happen and what is happening from your post. Is it possible there's an error in your BoothRepository.Find method such that it returns the same thing every time?
ModelBinder should not be affecting this method because the parameter to the action method is a simple type, int.
Were both of these requests GET requests? If you still are having problems, can you try and create the simplest repro possible and email it to philha - microsoft dot com?
EDIT: The problem ended up being that the developer was attempting to re-use the valueprovider across requests (by having Castle Windsor manage the lifecycle of Controllers). Right now, there's no support for re-using controller instances across requests like you would with IHttpHandler which has a IsReusable property. So in general, reusing controllers across requests requires doing a lot more work on your end. :)
The problem is the LifeStyle, I completetly overlooked the fact it was being defined, which means by default the controllers will use the Singleton lifestyle. Setting the LifeStyle to Transient for all controllers will sort this problem.
if you use spring.net modify
Controller's singleton to "false"
This is a common issue when using Singleton behavior with a IoC container such as Spring.NET or Windsor. Controllers should not have singleton behavior because the ControllerContext is per request, much like HttpContext.