Is there any way to gain access to the controller that is currently executing from within Global.asax?
I'd like to design an API with similar syntax to:
MyClass.RegisterComponents().When(IController => /* Some condition */)
Although I could move this code to a place where the controller is in context, I'd like to keep it centralised and portable.
So far, I have been unable to obtain the controller. Any ideas?
I have considered creating a base controller and extending all of my controllers from this base class, however, I'd like to make this library portable with the ability to be installed via NuGet. For this reason I am unable to take this approach.
You can do following in your global.asmx file.
private void Application_BeginRequest(object sender, EventArgs e)
{
string controllerName = Request.RequestContext.RouteData.Values.Where(p => p.Key =="controller").FirstOrDefault(p => p.Key);
}
I found a solution to this. Not a very good one but it solves my problem.
Register a global IActionFilter using an assembly start up method I found on David Ebbo's blog (http://blog.davidebbo.com/2011/02/register-your-http-modules-at-runtime.html).
The global action filter simply stores the action context in the current HttpContext.Items[] collection which is a per request collection.
public class GlobalActionFilter : System.Web.Mvc.IActionFilter {
internal static readonly object ActionExecutedFilterKey =
"__MvcResourceLoaderActionExecutedContext";
internal static readonly object ActionExecutingFilterKey =
"__MvcResourceLoaderActionExecutingContext";
static MvcResourceLoaderGlobalFilter __instance =
new MvcResourceLoaderGlobalFilter();
MvcResourceLoaderGlobalFilter() { }
public void OnActionExecuted(System.Web.Mvc.ActionExecutedContext filterContext) {
System.Web.HttpContext.Current.Items[ActionExecutedFilterKey] =
filterContext;
}
public void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) {
System.Web.HttpContext.Current.Items[ActionExecutingFilterKey] =
filterContext;
}
public static void RegisterGlobalFilter() {
if (!System.Web.Mvc.GlobalFilters.Filters.Contains(__instance))
System.Web.Mvc.GlobalFilters.Filters.Add(__instance);
}
}
I can then access the context anywhere.
Related
Our company has the need to log certain things each time one of our action methods of our ASP.NET WebApi controllers gets called. Since we use Ninject for the DI right now, we'd like to use it also for this purpose. This is what I have tried so far.
I have Ninject, Ninject.Extensions.Interception and Ninject.Extensions.Interception.DynamicProxy installed through NuGet and I have the following module
public class InterceptAllModule : InterceptionModule
{
public override void Load()
{
Kernel.Intercept(p => p.Request.Service.Name.EndsWith("Controller")).With(new TimingInterceptor());
}
}
Where TimingInterceptor is
public class TimingInterceptor : SimpleInterceptor
{
readonly Stopwatch _stopwatch = new Stopwatch();
protected override void BeforeInvoke(IInvocation invocation)
{
_stopwatch.Start();
}
protected override void AfterInvoke(IInvocation invocation)
{
_stopwatch.Stop();
string message = string.Format("[Execution of {0} took {1}.]",invocation.Request.Method,_stopwatch.Elapsed);
Log.Info(message + "\n");
_stopwatch.Reset();
}
}
Now, when I try to hook the module up with ninject kernel, and run my site
var kernel = new StandardKernel(new InterceptAllModule());
However, whenever there is a call coming in to one of the action method, it throws an error saying
Cannot instantiate proxy of class: MyApiController.
Could someone with experience point out what I'm doing wrong please? Thanks.
Update
So using your Code and Remo's excellent point about needing the action methods to be virtual and putting in an empty default constructor (just to placate dynamic proxy, keep your other constructor still) I have got both the action filter and the interception approach working.
I would say that as it stands your code will intercept potentially unwanted methods on the ApiController so you will probably also need to put some code in place to filter these out e.g. ExecuteAsync and Dispose.
My only other point is performance. Huge disclaimer these are just very basic tests (using the action filter approach each time to log the stats), I invite you to do your own(!)... but using the DynamicProxy interceptor I was getting a time of around 4 milliseconds per get request
[Execution of Get took 00:00:00.0046615.]
[Execution of Get took 00:00:00.0041988.]
[Execution of Get took 00:00:00.0039383.]
Commenting out the Interception code and using an Action filter I was getting sub millisecond performance:
[Execution of Get took 00:00:00.0001146.]
[Execution of Get took 00:00:00.0001116.]
[Execution of Get took 00:00:00.0001364.]
It is up to you whether this is actually an issue or concern but I thought I would point this out.
Previous Response
Have you rulled out using ActionFilters? This is the natural extension point for AOP on an MVC action.
If you were interested in methods other than the actual action on the controller then I would understand but I thought I would post a suggestion anyway.
Inspired by Are ActionFilterAttributes reused across threads? How does that work? and Measure Time Invoking ASP.NET MVC Controller Actions.
Updated to show the exclusion of the timer when method tagged. Inspiration from core WebApi framework specifically AllowAnonymousAttribute and AuthorizeAttribute
Register this globally so that all actions are monitored by this:
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
Then:
public class TimingActionFilter : ActionFilterAttribute
{
private const string Key = "__action_duration__";
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (SkipLogging(actionContext))
{
return;
}
var stopWatch = new Stopwatch();
actionContext.Request.Properties[Key] = stopWatch;
stopWatch.Start();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (!actionExecutedContext.Request.Properties.ContainsKey(Key))
{
return;
}
var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch;
if(stopWatch != null)
{
stopWatch.Stop();
var actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
Debug.Print(string.Format("[Execution of {0} took {1}.]", actionName, stopWatch.Elapsed));
}
}
private static bool SkipLogging(HttpActionContext actionContext)
{
return actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>().Any() ||
actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<NoLogAttribute>().Any();
}
}
And
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoLogAttribute : Attribute
{
}
Now you can exclude the global filter using:
public class ExampleController : ApiController
{
// GET api/example
[NoLog]
public Example Get()
{
//
}
}
For anyone still lurking, the reason I wanted to use Ninject was so I could inject a logger (or anything else) into the interceptor, but I wanted to intercept all actions.
Mark's answer is perfect, but instead of registering globally using
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
bind your filter with Ninject using
Kernal.BindHttpFilter<TimingActionFilter>(FilterScope.Action).
You'll need to create an appropriate contructor in the TimingActionFilter class.
While very familiar to Webforms and Linq, I am a novice to the ASP.NET MVC and NHibernate World. I have been working through a project using Bob Cravens' examples. My application is mostly reads and non-sequential writes, so typically I would not use a transactions. But to implement the Unit-of-Work pattern, all my research including Ayende's blog says I should.
The problem I have is this -
Ninject creates a Session and Opens a Transaction.
Ninject injects repositories into services, and services into controllers.
I make some changes to the properties and children of an object and save on the aggregate root. This calls Transaction.Commit (works fine, as expected)
In another method later in the controller, I try to save a separate object
The second call fails because the transaction is no longer active.
I'm thinking of adding a bool "CommitNeeded" to the UnitOfWork which would be set by my Save() method and conditionally trigger a Commit() on UnitOfWork.Dispose(). Is this a good idea?
Should I remove the transaction infrastructure? Should I change my Commit()'s to Flush()'s?
Any advice that would help fix my anti-pattern would be appreciated.
In response to the comments - I guess I don't mind if they happen together or separate. There are two things going on. The first one changes a "Customer" object, and then saves it. The second makes a logging entry which then calls the same "Save" method.
var Customer = ServiceLayer.GetCustomer(...);
Transmogrify(Customer, Customer.Children, Widgets, ...);
ServiceLayer.Save(Customer)
ServiceLayer.RecordEvent(Customer, "Customer was frobbed")
where LogEvent looks like
public void RecordEvent(Customer customer, int eventTypeId, string description)
{
...
Save(customer);
}
The RecordEvent method has its own "save" because it is called from other controllers that do no data changes. I believe the Save call doesn't belong in either of those places. The question is, where? The Dispose() method of the Service Layer? or a Filter like the other users suggested?
Using ASP.NET MVC, I use an action filter to bind the transaction scope to the controller action execution lifecycle. This works great most of the time, but you have to be cautious not to keep transactions open too long.
public class UnitOfWorkActionFilter : ActionFilterAttribute
{
public IUnitOfWork UnitOfWork { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
UnitOfWork.Start();
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception == null)
{
UnitOfWork.Commit();
}
else
{
UnitOfWork.Rollback();
}
UnitOfWork.Dispose();
base.OnActionExecuted(filterContext);
}
}
In my case I'm using property injection via a custom ControllerActionInvoker to get the IUnitOfWork dependency into the ActionFilterAttribute.
I'm using for that an http module. I get the transaction at the beginning of http request and terminate it on the end of http request :
public class UnitOfWorkModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
context.EndRequest += context_EndRequest;
}
private void context_BeginRequest(object sender, EventArgs e)
{
IUnitOfWork instance = UnitOfWorkFactory.GetDefault();
instance.Begin();
}
private void context_EndRequest(object sender, EventArgs e)
{
IUnitOfWork instance = UnitOfWorkFactory.GetDefault();
try
{
instance.Commit();
}
catch
{
instance.RollBack();
}
finally
{
instance.Dispose();
}
}
}
My unit of work factory it's just a Func initialized while registering types in IoC container :
public class UnitOfWorkFactory
{
public static Func<IUnitOfWork> GetDefault;
}
Initialization (for my case StructureMap) :
UnitOfWorkFactory.GetDefault = () => container.GetInstance<IUnitOfWork>();
And then you register you in UnitOfWorkModule web.config
<httpModules>
<add name="UnitOfWorkModule" type="UI.UnitOfWorkModule, UI" />
</httpModules>
I am attempting to redesign an existing application using dependency injection with Ninject in MVC3. Here is a portion of the legacy behavior I'm having difficulty with (and yes I know its bad, that's why I'm trying to refactor it):
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
MyUserSession userSession = filterContext.HttpContext.Session[SESSIONKEY_USER] as MyUserSession;
// if session empty, rebuild user information
if (userSession == null)
{
string userName = HttpContext.User.Identity.Name;
userSession = new MyUserSession();
using (ADSearcher ad = new ADSearcher(ldapPath, excludeOUString.Split(',')))
{
// get basic user information from Active Directory
ADUserInfo aduser = MyActiveDirectorySearcher.GetUserRecord(userName);
// ... set several properties queries from AD...
userSession.propertyXYZ = aduser.propXYZ
}
// if user can proxy as another indivudual, set property
using (EDMContainer db = new EDMContainer())
{
if (db.Proxies.Any(p => p.ProxyLogin == userSession.userLogin))
userSession.CanProxy == true;
}
// save new user object to session
filterContext.HttpContext.Session[SESSIONKEY_USER] = userSession;
if(userSession.canProxy)
filterContext.Result = RedirectToAction("Proxy", "Home");
return;
}
}
So currently, the controller users several objects directly: Session, ActiveDirectorySearch, EF Database. I understand it would be better to create a class that exposes a single method "GetUser" masking all the complexity but I'm struggling with how to inject the dependencies.
If I create a class SomeUserProvider, it will also need access to the Session to check for existing user information, and then ActiveDirectorySearcher and Database to rebuild the user properties if session was empty.
My confusion is over the fact that the controller itself will need access to ActiveDirectorySearcher in other action methods and then other classes will also use the same database. Do I inject an IActiveDirSearchrer into the controller's constructor and then pass it down into the ISomeUserProvider? What about IMyDatabase? Is it also injected in controller constructor and passed down?
And last but not lease, ISessionWrapper? I know session is controversial, but I need to track who the current user is and who they are proxied as during each request (GETs and POSTs). So, does that get injected as well?
If the answer is yes to each of those, is it bad to have 3+ injected contstuctor parameters?
I realize my question may be vague, so please ask for clarification where needed. I am open to any and all suggestions and recommendations. My goal is to learn how to do it correctly.
Thanks.
I'm not certain if this is exactly what you're looking for, but this should get you started down the path of refactoring your app for DI
public class YourController : Controller
{
private readonly ISessionWrapper _sessionWrapper;
private readonly IActiveDirSearcher _adSearcher;
private readonly IMyDatabase _database;
public YourController(ISessionWrapper sessionWrapper,
IActiveDirSearcher adSearcher, IMyDatabase database)
{
this._sessionWrapper = sessionWrapper;
this._adSearcher = adSearcher;
this._database = database;
}
// now all actions in this controller have a _sessionWrapper,
// _adSearcher and _database
}
Then you have to bind your injections the Ninject way. Subclass your application from NinjectHttpApplication and override OnApplicationStarted and CreateKernel
public class MvcApplication : NinjectHttpApplication
{
// ...
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<ISessionWrapper>().To<YourSessionWrapperImplementation>();
kernel.Bind<IActiveDirSearcher>().To<YourADImplementation>();
kernel.Bind<IMyDataBase>().To<YourEDMContainerIThink>();
return kernel;
}
}
The implementations of these appear to be described in your question. However, you mentioned other actions (and other classes) depend on these implementations. Good news--the bindings in CreateKernel will take care of any missing dependencies elsewhere in your app. e.g.
public class MyActiveDirImplementation : IActiveDirSearcher
{
private readonly IMyDatabase _database;
// injected automagically WOOHOO!
public MyActiveDirImplementation(IMyDatabase database)
{
this._database = database;
}
public ADUserInfo GetUserRecord(string username)
{
return _database.GetSomeUserRecord(username);
}
}
You could, of course, similarly implement your ISessionWrapper or IMyDatabase
I'm try to build a static property on a static class that will basically return a cookie value, to be used across my MVC site (MVC 3, if it matters). Something like this:
public static class SharedData
{
public static string SomeValue
{
get
{
if (HttpContext.Current.Request.Cookies["SomeValue"] == null)
{
CreateNewSomeValue();
}
return HttpContext.Current.Request.Cookies["SomeValue"].Value.ToString();
}
}
}
I need to access this from within controller actions, global.asax methods, and action filters. But the problem is, when action filters run, HttpContext is not available. Right now, I have to have a separate static method just to pull the cookie from the filter context that I pass in, which seems awkward.
What is the best solution for building such a static method for retrieving a cookie value like this that works from both controller actions and action filters? Or is there a better approach for doing something like this?
Thanks in advance.
The call to the static HttpContext.Current is not good design. Instead, create an extension method to access the cookie from an instance of HttpContext and HttpContextBase.
I wrote a little helper for you. You can use it to perform your functionality from within an action filter.
public static class CookieHelper
{
private const string SomeValue = "SomeValue";
public static string get_SomeValue(this HttpContextBase httpContext)
{
if(httpContext.Request.Cookies[SomeValue]==null)
{
string value = CreateNewSomeValue();
httpContext.set_SomeValue(value);
return value;
}
return httpContext.Request.Cookies[SomeValue].Value;
}
public static void set_SomeValue(this HttpContextBase httpContext, string value)
{
var someValueCookie = new HttpCookie(SomeValue, value);
if (httpContext.Request.Cookies.AllKeys.Contains(SR.session))
{
httpContext.Response.Cookies.Set(someValueCookie);
}
else
{
httpContext.Response.Cookies.Add(someValueCookie);
}
}
}
Note: You could easily make these methods work on HttpContext instead just by replacing the HttpContextBase parameter with HttpContext.
As JohnnyO pointed out above, I had access to HttpContext from within my action filter all along. At least, in the particular action filter method where this was needed. There may have been some other filter/method that did not have access at one point, but for now, this is working as I need it to.
I have an external-to-my-solution web service that I'm using in an ActionFilter. The action filter grabs some basic data for my MasterPage. I've gone back and forth between using an action filter and extending the base controller class, and decided the action filter was the best approach. Then I started unit testing (Yeah, yeah TDD. Anyway... :D )
So I can't mock (using Moq, btw) a web service in an action filter because I can't inject my mock WS into the action filter, since action filters don't take objects as params. Right? At least that's what I seem to have come to.
Any ideas? Better approaches? I'm just trying to return a warning to the user that if the web service is unavailable, their experience might be limited.
Thanks for any help!
namespace MyProject.ActionFilters
{
public class GetMasterPageData : ActionFilterAttribute
{
public ThatWS ws = new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContextBase context = filterContext.HttpContext;
try {
DoStuff();
}
catch ( NullReferenceException e ) {
context.Session["message"] = "There is a problem with the web service. Some functionality will be limited.";
}
}
}
}
Here's a quick and dirty approach:
public class GetMasterPageData : ActionFilterAttribute
{
public Func<ISomeInterface> ServiceProvider = () => new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var result = ServiceProvider().SomeMethod();
...
}
}
And in your unit test you could instantiate the action filter and replace the ServiceProvider public field with some mocked object:
objectToTest.ServiceProvider = () => new SomeMockedObject();
Of course this approach is not as clean as the one suggested by #Ryan in the comments section but it could work in some situations.