HttpContext on instances of Controllers are null in ASP.net MVC - asp.net-mvc

This may not be the correct way to use controllers, but I did notice this problem and hadn't figured out a way to correct it.
public JsonResult SomeControllerAction() {
//The current method has the HttpContext just fine
bool currentIsNotNull = (this.HttpContext == null); //which is false
//creating a new instance of another controller
SomeOtherController controller = new SomeOtherController();
bool isNull = (controller.HttpContext == null); // which is true
//The actual HttpContext is fine in both
bool notNull = (System.Web.HttpContext.Current == null); // which is false
}
I've noticed that the HttpContext on a Controller isn't the "actual" HttpContext that you would find in System.Web.HttpContext.Current.
Is there some way to manually populate the HttpContextBase on a Controller? Or a better way to create an instance of a Controller?

For now I'm going to do the following. This seems to be an acceptable fix...
public new HttpContextBase HttpContext {
get {
HttpContextWrapper context =
new HttpContextWrapper(System.Web.HttpContext.Current);
return (HttpContextBase)context;
}
}
Where this is added to a Controller class these Controllers are inheriting from.
I'm not sure if the HttpContext being null is the desired behavior, but this will fix it in the meantime for me.

Controllers are not designed to be created manually like you're doing. It sounds like what you really should be doing is putting whatever reusable logic you have into a helper class instead.

The HttpContext, in the ControllerContext is null because it is not set when the controller is created. The contructor of the controller does not assign this property, so it will be null. Normally, the HttpContext is set to the HttpContext of the ControllerBuilder class. Controllers are created by the ControllerBuilder class, followed by the DefaultControllerFactory. When you want to create your own instance of a controller, you can use the ExecuteMethod of the controller with your own ControllerContext. You don't want to do that is a real application. When you get some more experience with the framework you will find the appropriate method to do want you want. When you need ControllerContext in Unit test, you can use a mocking framework to mock the ControllerContext or you can class faking it.
You can find a model of the request flow in asp.net mvc on this blog.
When your new to Asp.net mvc, it's worth the effort to download the source code and read an trace the route how a request is processed.

Is it that you want to use some functionality from the controller? Or have the controller perform an action?
If it's the former, maybe that's some code that should be split out into another class. If it's the latter, you can do this to simply have that controller do a specific action:
return RedirectToAction("SomeAction", "SomeOtherController", new {param1 = "Something" });

Are you using a controller factory? If so, how are you registering components?
I ran into this problem where I had inadvertently added an HttpContext-based dependency as a Singleton, rather than Transient in Windsor.
HttpContext was null for all but the first request. It took me a while to track down that one.

Related

Can I access the ViewData from the HttpContext?

Im working with a project that sets variables such as the current user profile object in its authorize action filter, storing them in the ViewData for access by the following action method.
The action method then calls functionality from the repository. I'm trying to find a way to access the ViewData from the repository WITHOUT modifying the repository's method signature, and am hoping there is a way I can track back to it via the HttpContext.Current functionality which I can call from the repository.
Can anyone help with this? Just to be clear, the only code that I can modify is within the repository method :(
public class MyController : Controller {
[MyAuthorize] // ViewData items are set here
public void MyAction(int id)
{
new MyRepository().DoSomething(id); // Need to access ViewData items within this repository method and am unable to alter the method signature :(
}
}
I'm pretty sure the answer is "no".
When you review the ASP.NET MVC source code, ControllerBase instantiates a ViewData dictionary on first use. Then when you call View(), a new ViewResult is instantiated with the ControllerBase.ViewData dictionary as a parameter. It does not look like it gets applied to a public static property or class like HttpContext which you could access from inside your repository.
I think your best bet would be to use HttpContext.Items which is built for this type of communication. Though probably not as ideal as just modifying the repository to accept the extra data.

Checking object access using ActionFilters

I have been happily using a BaseController class with a custom ActionFilterAtribute, overriding OnActionExecuting(). I use this BaseController class for all my controllers as a convenient way of checking the id route value when it is passed in to the Controller.
This worked fine... I check for an id value, and if it exists I check to see the controller name which determines the type of object that the id belongs to.
But then... I started to get errors which are created when a PartialView is called in ControllerB and presenting the same id in the FilterContext that was used for the parent Controller/View. For example say I have a url of localhost/Project/Details/5 where Project is the controller and Details the action with an id=5. Controller "Notes" gets called due to a PartialView and my ActionFilter is triggered this time for Controller "Notes" and the original "Project" id. The user is permitted access to ProjectId=5 but not NoteId=5
I hope this makes sense - I am struggling to explain. I now need to either find some way of ignoring the PartialView, or handling it, or using a different approach altogether. My end goal is to be able to check object access in my application based on the id of an object. Is there a "best approach" for this kind of problem? Maybe I need to write a filter for every controller? Or possibly check the Action name as well so I know when to ignore "id"?
My BaseController...
public class AccessCheckAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int id;
if (filterContext.RouteData.Values["id"] != null)
{
if (int.TryParse(filterContext.RouteData.Values["id"].ToString(), out id))
{
SPTdb db = new SPTdb();
switch (filterContext.RouteData.Values["Controller"].ToString().ToLower())
{
case "project":
project = db.Projects.Find(id);
I think this problem can be solved by using SQL query. You pass the table name and also with [key/value] of your object identity.

Share session between two MVC applications

I have an older application that has an ActionResult function that I would like to include in another newer application.
I have found that I can add the older controller as a reference, Instantiate the controller as an object, and then call the ActionResult function accordingly, like so:
public ActionResult test()
{
OlderApplication.Controllers.PatronController temp = new OlderApplication.Controllers.PatronController();
return temp.Index();
}
My problem lies in the fact that inside temp.Index() it references the Session, and it is coming up as null instead of having a value that should exist. Can I make session available this way?
In the example you've given you don't have two MVC applications - you have a single application that shares the same codebase. An "application" is defined by the webserver, but I digress.
The reason the Session within temp is null is because you aren't initializing the Controller correctly - Controllers are not POCOs, they require initialization. Call temp.Initialize(), however you'll need to create the RequestContext instance yourself, like so:
RequestContext context = new RequestContext( this.Context, this.RouteData );
OlderApplication.Controllers.PatronController oldController = new OlderApplication.Controllers.PatronController();
oldController.Initialize( context );
return oldController.Index();
Although if you're going through this step, you might as well just wire up your old controller into your Area Registration and/or URI Routing tables, thus negating the need to write this code.
As Dai pointed out you're not creating the controller correctly.
I would suggest using the controller factory to create your controller.
var oldController = ControllerBuilder.Current.GetControllerFactory().CreateController(Request.RequestContext, "Patron");

Dependency Injection into an MVC action method

I'm wondering if this is possible. I have a typical MVC action method with a signature that looks like this:
public ActionResult View(MyModel model)
{
IAnObject myObject = new AnObject();
//several lines of code follow.....
return View(model);
}
I'd like to get rid of that new keyword and inject an instance of IAnObject into the action method. But I'm not sure if MVC allows for this, injecting a class along side a model in an action method? Has anyone run across this, and are there ways of tackling it? (Our IoC container is Windsor, in case that makes a difference.)
If you are expecting to inject this reference into the action method as a parameter, you can look to the ControllerActionInvoker, which has an InvokeActionMethod method, which I believe is called from InvokeAction. This method has a list of parameters passed into it, and a description of the action (ActionDescriptor class). This action descriptor has a GetParameters method that will give you more detailed information about the parameter, such as type information that you would need for the dependency injector. I've not done this, so I don't know quite how it works out, but it seems possible.
I also don't know how that might affect how MVC selects an action method to post to, so factor that in.
You may want to do your injection in OnActionExecuting which is called before any action on the controller is executed. This will give you context such as the Request but will allow you to set member variables - thus 'simulating' constructor injection. And of course you only have to do it once for the whole controller.
[NonAction]
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService = .........; // get from IoC container
base.OnActionExecuting(filterContext);
}
Well, I agree with the guys on the comments, but if you want to take an instance in the method scope, try to get it from your container of IoC, something like this:
public ActionResult View(MyModel model)
{
// take from the container of IoC
IAnObject myObject = _continerIoC.Resolve<IAnObject >();
//several lines of code follow.....
return View(model);
}
Avoid using the new to create your instance and your concrete type in the container and decouple your controller from dependecies/references.
I really consider using constructor/property Injection. There is a method injection too.

ASP.NET MVC - Set ViewData for masterpage in base controller

I'm using a masterpage in my ASP.NET MVC project. This masterpage expects some ViewData to be present, which displays this on every page.
If I don't set this ViewData key in my controllers, I get an error that it can't find it. However, I don't want to set the ViewData in every controller (I don't want to say ViewData["foo"] = GetFoo(); in every controller).
So, I was thinking of setting this in a base controller, and have every controller inherit from this base controller. In the base controller default constructur, I set the ViewData. I found a similar approach here: http://www.asp.net/learn/MVC/tutorial-13-cs.aspx. So far so good, this works... but the problem is that this data comes from a database somewhere.
Now when I want to Unit Test my controllers, the ones that inherit from the base controller call its default constructor. In the default constructor, I initialize my repository class to get this data from the database. Result: my unit tests fail, since it can't access the data (and I certainly don't want them to access this data).
I also don't want to pass the correct Repository (or DataContext, whatever you name it) class to every controller which in turn pass it to the default controller, which I could then mock with my unit tests. The controllers in turn rely on other repository classes, and I would end up passing multiple parameters to the constructor. Too much work for my feeling, or am I wrong? Is there another solution?
I've tried using StructureMap but in the end I didn't feel like that is going to fix my problem, since every controller will still have to call the base constructor which will initialize the repository class, so I can't mock it.
This is a similar question but I find no satisfactory answer was given. Can I solve this in a neat way, maybe using StructureMap as a solution? Or should I jsut suck it and pass a Repository to every controller and pass it again to the base controller? Again, It feels like so much work for something so simple. Thanks!
I see two options:
First:
Set the ViewData for MasterPage in YourBaseController.OnActionExecuting() or YourBaseController.OnActionExecuted():
public class YourBaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Optional: Work only for GET request
if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
return;
// Optional: Do not work with AjaxRequests
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
return;
...
filterContext.Controller.ViewData["foo"] = ...
}
}
Second:
Or create custom filter:
public class DataForMasterPageAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Optional: Work only for GET request
if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
return;
// Optional: Do not work with AjaxRequests
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
return;
...
filterContext.Controller.ViewData["foo"] = ...
}
}
and then apply to your controllers:
[DataForMasterPage]
public class YourController : YourBaseController
{
...
}
I think the second solution is exactly for your case.

Resources