I've created an application controller abstract class that my controllers derive from (As described in the following article)
The following is an example of what my code looks like
public abstract class ApplicationController : Controller
{
private ProjectDataContext datacontext = new ProjectDataContext();
protected ProjectDataContext DataContext
{
get { return datacontext; }
}
public ApplicationController()
{
ViewData["OpenTasks"] = DataContext.Tasks.Where(t => t.UserId == this.UserId).Count();
}
}
This produces the following error which i have determined is due to the "Where" lamda expression:
If the controller doesn't have a controller factory, ensure that it has a parameterless public constructor.
this error is produced whichever way i write the LINQ query and the only way to compile the application is by removing the "Where" clause as follows.
ViewData["OpenTasks"] = DataContext.Tasks.Count();
any ideas what the problem is or how to resolve this as i need to execute the query against the user and not return all entries.
thanks in advance
Try this instead of using the constructor:-
public abstract class ApplicationController : Controller
{
private ProjectDataContext datacontext = new ProjectDataContext();
protected ProjectDataContext DataContext
{
get { return datacontext; }
}
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(RequestContext);
ViewData["OpenTasks"] = DataContext.Tasks.Where(t => t.UserId == this.UserId).Count();
}
}
Its quite likely that the current user ID is dependand on the RequestContext
It could be that the call is crashing in the constructor because the UserId (this.UserId) hasn't been initialised yet.
Related
I am trying to make sure that my MVC application only uses one DbContext per Request in order to reduce number of times a Db connection is open and so there are no concurrency issues.
This means i will need to use the same context in my Global Action Filters as well as my Controllers.
I have tried something like this
public class LayoutFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MembershipUser loggedInUser = Membership.GetUser();
MyUnitOfWork uow = new MyUnitOfWork();
ViewBag.FullName = uow.UserService.GetUser().FullName
filterContext.ActionParameters["unitOfWork"] = uow;
}
}
However the context is disposed when i try to read it from the controller as shown below
public ActionResult Logout(MyUnitOfWork uow)
{
ViewBag.Something = uow.ExampleService.GetMyObject();
return RedirectToAction("LogIn");
}
I get the same issue with the context being disposed when i try to share the same unitOfWork object by casting a property of a base controller class
public class BaseController : Controller
{
public RequestboxUnitOfWork unitOfWork;
}
public class LayoutFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MembershipUser loggedInUser = Membership.GetUser();
BaseController baseController = (BaseController)filterContext.Controller;
ViewBag.FullName = baseController.unitOfWork.UserService.GetUser().FullName
filterContext.ActionParameters["unitOfWork"] = uow;
}
}
The context is disposed when i try to access it in the controller and i have read in a few places that you should not use base controller class so i am unsure of what i can do.
What are the recommended ways to share a entity context between ActionFilters and Controllers
Create the DBContext as part of the Controller setup, and have it available via an internal property on the controller.
public class MyController : Controller
{
private MyUnitOfWork unitOfWork;
internal MyUnitOfWork UnitOfWork
{
get { return unitOfWork; }
}
}
You will then be able to access the context in the filter attribute like this:
MyController controller = (MyController)filterContext.Controller
MyUnitOfWork uow = controller.UnitOfWork;
There's no need to pass the unit of work back to the action method in the controller, because the controller already has the object, and it can be accessed via the same internal property.
I need to setup a policy in base controller that applies to all controller instance, like below:
public class BaseController : Controller
{
private IPolicy Policy;
public BaseController()
{
this.Policy= new Policy(HttpContext);
}
}
Within the Policy class, I need to do something like:
this.httpContextBase.User.
Questions: (Update)
What is the better way to design the BaseController in terms of using HttpContext and Unit test.
What is the correct way to unit test HttpContext?
Absolutely no way. You are using the HttpContext inside the constructor of a controller when this context is still not initialized. Not only that this code cannot be tested but when you run the application it will also crash with NRE. You should never use any HttpContext related stuff in a constructor of a controller.
One possibility is to refactor your code and perform this inside the Initialize method:
public class BaseController : Controller
{
private IPolicy Policy;
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
this.Policy = new Policy(HttpContext);
}
}
This being said, that's not the approach I would recommend. I would recommend you using dependency injection instead of service location which is considered by many as an anti-pattern.
So:
public abstract class BaseController : Controller
{
protected IPolicy Policy { get; private set; }
protected BaseController(IPolicy policy)
{
Policy = policy;
}
}
Now, all that's left is to configure your favourite Dependency Injection framework to inject the correct instance into the constructor. For example with Ninject.Mvc3 this is achieved with a single line of code:
kernel.Bind<IPolicy>().To<Policy>();
Now you can feel more than free to mock this IPolicy in your unit test without even caring about any HttpContext.
For example let's suppose that you have the following controller that you want to unit test:
public class FooController : BaseController
{
public FooController(IPolicy policy): base(policy)
{ }
[Authorize]
public ActionResult Index()
{
Policy.DoSomething();
return View();
}
}
Now, all that you need to do is pick up your favorite mock framework (Rhino Mocks in my case) and do the mocking:
[TestMethod]
public void Index_Action_Should_DoSomething_With_The_Policy()
{
// arrange
var policyStub = MockRepository.GenerateStub<IPolicy>();
var sut = new FooController(policyStub);
// act
var actual = sut.Index();
// assert
Assert.IsInstanceOfType(actual, typeof(ViewResult));
policyStub.AssertWasCalled(x => x.DoSomething());
}
Some background to my issue:
It appears that there is a change/bug in Mvc2 concerning ValidateAntiForgeryTokenAttribute.
When upgrading from Mvc1 to Mvc2, users with an active session will receive the following error when they request a page using ValidateAntiForgeryTokenAttribute:
Unable to cast object of type 'System.Web.UI.Triplet' to type 'System.Object[]'.
The issue is documented here.
After upgrading to Mvc2, we anticipate that we will be seriously affected by this issue. I have coded a fix derived from the code in the comments (documented below for posterity). At the moment this code is invoked by creating a subclass of Controller and AsyncController overriding the Initialize method in order to correct the problem. e.g.
public class FixedController:Controller
{
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
this.FixAntiForgeryTokenMvc1ToMvc2(requestContext); //extension
}
}
internal static class ControllerEx
{
public static void FixAntiForgeryTokenMvc1ToMvc2(
this Controller controller,
RequestContext requestContext)
{
var cc = new ControllerContext(requestContext,
controller);
var antiForgeryAttribute = new ValidateAntiForgeryTokenAttribute();
try
{
antiForgeryAttribute.OnAuthorization(new AuthorizationContext(cc));
}
catch (HttpAntiForgeryException forgeryException)
{
var castException = forgeryException.InnerException;
if (castException != null
&& castException is InvalidCastException
&& castException.Message.StartsWith(
"Unable to cast object of type"
+ " 'System.Web.UI.Triplet' to type"
+ " 'System.Object[]'"))
{
var responseTokenCookieNames =
controller
.Response
.Cookies
.Cast<Cookie>()
.Select(c => c.Name)
.Where(n => n.Contains("RequestVerificationToken"));
foreach (var s in responseTokenCookieNames)
{
var cookie = controller.Response.Cookies[s];
if (cookie != null)
{
cookie.Value = "";
}
}
var requestTokenCookieNames =
controller
.Request
.Cookies
.Cast<String>()
.Where(n => n.Contains("RequestVerificationToken"))
.ToList();
foreach (var c in requestTokenCookieNames)
{
controller.Request.Cookies.Remove(c);
}
}
}
}
}
The knock on effect of this is that I must alter all my controller classes to derive from my new, corrected Controller subclasses. This seems quite intrusive for code that intend to deprecate in about a month's time.
So, coming to my question, I'd like to know if there is a less intrusive means of patching existing classes such that downstream users of the class do not have to be altered, perhaps using reflection?
Spender,
rather than code every controller, use a basecontroller and inherit from that along the lines of:
public abstract class BaseController : Controller
{
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
FixAntiForgeryTokenMvc1ToMvc2(this, requestContext);
}
private static void FixAntiForgeryTokenMvc1ToMvc2(
Controller controller, RequestContext requestContext)
{
// stuff ....
}
}
and in your 'normal' controllers, just have:
public class NormalController : BaseController
{
// all the previous stuff
}
give it a go...
I'm following this ASP.NET MVC tutorial from Microsoft:
My code is slightly different, where I'm trying to access HttpContext.Request.IsAuthenticated in the controller's constructor.
namespace SCE.Controllers.Application
{
public abstract class ApplicationController : Controller
{
public ApplicationController()
{
bool usuario = HttpContext.Request.IsAuthenticated;
}
}
}
The problem is that HttpContext is always null.
Is there a solution to this?
instead of putting your HttpContext.Request.IsAuthenticated in Controller level you should put it in Controller Base class that will be inherited in all of your controller with an override method of OnActionExecuting() method.
In your Controller base you should have
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext ctx) {
base.OnActionExecuting(ctx);
ViewData["IsAuthenticated"] = HttpContext.Request.IsAuthenticated;
}
}
and all your Controller should inherit the BaseController class
public class ApplicationController : BaseController
now you should get the ViewData["IsAuthenticated"] in your Master page.
Edit
With the link you have given, and relating to what you have done, your ApplicationController is a Page Controller, not a Base Controller. In the example, ApplicationController is a Base Controller that is inherited by the HomeController but what you have done is you are placing the Action method inside your base controller which is the ApplicationController so your Action Index method will not be invoked when you call any page (Index page) that is not from the ApplicationController.
I would suggest you use:
System.Web.HttpContext.Current.Request
Just remember System.Web.HttpContext.Current is threadstatic, but if you don't use additional thread the solution works.
The Controller is instantiated significantly prior to the point where the Index action is invoked, and at the moment of construction HttpContext is indeed unavailable. What's wrong with referencing it in your controller method Index?
The solution of this problem is to create an override method of Initialize by passing RequestContext object.
public class ChartsController : Controller
{
bool isAuthed = false;
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
if (requestContext.HttpContext.User.Identity.IsAuthenticated)
{
isAuthed =true;
}
}
}
With the answer I am posting here, you cannot access IsAuthenticated, but you can access some stuffs related to HttpContextRequest (see in image),
I needed session value in constructor.
You can use IHttpContextAccessor as below:
public ABCController(IHttpContextAccessor httpContextAccessor)
{
//do you stuff with httpContextAccessor,
// This gives session value
string abc = httpContextAccessor.HttpContext.Session.GetString("Abc");
}
and in startup.cs, you need to configure,
services.AddHttpContextAccessor();
It is possible to get the HttpContext using IHttpContextAccessor injected into class constructor. Before doing so, you will need first to register the corresponding service to the service container in Startup.cs class or Program.cs such as below.
services.AddHttpContextAccessor(); // Startup.cs
builder.Services.AddHttpContextAccessor(); // Program.cs
Right after that, you can inject the IHttpContextAccessor interface in whererever method or class constructor.
private bool isAuthenticated { get; set; }
public ConstructorName(IHttpContextAccessor accessor)
{
var context = accessor.HttpContext;
isAuthenticated = context.User.Identity.IsAuthenticated;
}
Here's my scenario:
I've successfully created a custom IIdentity that I pass to a GenericPrincipal. When I access that IIdentity in my controller I have to cast the IIdentity in order to use the custom properties. example:
public ActionResult Test()
{
MyCustomIdentity identity = (MyCustomIdentity)User.Identity;
int userID = identity.UserID;
...etc...
}
Since I need to do this casting for nearly every action I would like to wrap this functionality in an ActionFilterAttribute. I can't do it in the controller's constructor because the context isn't initialized yet. My thought would be to have the ActionFilterAttribute populate a private property on the controller that I can use in each action method. example:
public class TestController : Controller
{
private MyCustomIdentity identity;
[CastCustomIdentity]
public ActionResult()
{
int userID = identity.UserID;
...etc...
}
}
Question: Is this possible and how? Is there a better solution? I've racked my brain trying to figure out how to pass public properties that are populated in an attribute to the controller and I can't get it.
All you have to do is access the ActionExecutingContext of an overloaded OnActionExecuting() method and make identity public instead of private so your actionfilter can access it.
public class CastCustomIdentity : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
((TestController) filterContext.Controller).Identity = (MyCustomIdentity)filterContext.HttpContext.User;
base.OnActionExecuting(filterContext);
}
}
This could be even easier by using a custom base controller class that all of your controllers would inherit from:
public class MyCustomController
{
protected MyCustomIdentity Identity { get{ return (MyCustomIdentity)User.Identity; } }
}
and then:
public class TestController : MyCustomController
{
public ActionResult()
{
int userID = Identity.UserId
...etc...
}
}
You could use a custom model binder...
I can't remember why I used this method over the base controller method #jfar mentions (which is also a good option), but it works well for me and I actually kinda like it because my actions are more self describing through their parameters.
MyCustomIdentityModelBinder.cs
public class MyCustomIdentityModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.Model != null)
throw new InvalidOperationException("Cannot update instances");
//If the user isn't logged in, return null
if (!controllerContext.HttpContext.User.Identity.IsAuthenticated)
return null;
return controllerContext.HttpContext.User as MyCustomIdentity;
}
}
Inside your application start event in Global.asax.cs
System.Web.Mvc.ModelBinders.Binders.Add(typeof(MyCustomIdentity), new MyCustomIdentityModelBinder());
Then whenever you have a type of MyCustomIdentity as an action parameter, it'll automatically use the MyCustomIdentityModelBinder.
Eg.
public class TestController : Controller
{
public ActionResult Index(MyCustomIdentity identity)
{
int userID = identity.UserID;
...etc...
}
}
HTHs,
Charles