Skip to main content

Posts tagged with '.NET'

This is the last post of a series of posts about using ASP.NET's ActionFilter.

The last method that you can override in an ActionFilter is OnResultExecute, which runs after the result of a controller action has been executed.

An argument of type ResultExecutedContext gets passed in to OnResultExecute. It's not wildly different from ResultExecutingContext, except that instead of a "Cancel" member, you get a "Canceled" member, which just tells you after the fact if the action was cancelled or not. You can still access HttpContext, the Controller object, the Result, the RouteData, etc.

I can't really create another kitchen sink view like I could with the previous blog posts, because the ActionResult (usually a ViewResult) has already been executed. (I could still write to the Http Response, but that would be a little tedious).

Since OnResultExecuted can't output to the view, it's somewhat limited in its general usefulness. Logging, of course, can still be done here. Last minute changes to the HttpResponse might be a good use of this filter. You can still handle some exceptions, though using a HandleErrorAttribute filter might be a better idea for that.

Continuing my series on ASP.NET MVC ActionFilters, this time it's about the OnResultExecuting method (which runs before the result of the controller (e.g. ActionResult, ViewResult, etc) is run.

Since this filter runs after the action itself has completed, you won't be able to access any information about the action (except Route Data). The ResultExecutingContext has the basic context information that you've seen in the other contexts (HttpContext, Controller, RouteData), but it also has a "Cancel" bool property. If you set this property to true, the Result will not be executed (e.g. if it's a ViewResult, the View will not be returned to the browser; if it's a Redirect result, redirect headers will not be returned to the browser, etc). What you'll get by default is just a blank response with status code 200.

So why 'cancel' a result? You could substitute a cached view or replace it with some other response by directly manipulating the HttpContext. If the action returned a ViewResult, JsonResult, etc, you can still access the model passed to that result, to perform some logic or manipulate its properties.

As before, I created a kitchen sink view of ResultExecutingContext just for reference. I decided to put it in the ViewBag this time:

And here's a screenshot of the results in my browser:

OnResultExecuting Kitchen Sink

One more to go in this series. As always, if there's something that you want more information or details on, feel free to leave a comment or send a contact form to me, and I'll be glad to help out in any way I can.

Does PostSharp work on Windows 8 (or more correctly, with .NET 4.5)? The short answer is: not yet.

Long answer:

I tried to get it to work, I really did. I tried using the PostSharp Silverlight assembly. I tried installing with NuGet and I tried directly from the PostSharp installer. I tried it with a fox, I tried it on a box.

NuGet fail in VS11

I'm guessing that SharpCrafters is going to wait until Windows 8 / .NET 4.5 is more stable and closer to release before worrying about updating PostSharp. SharpCrafters is a very small company, so they probably can't afford to spend resources on updating their product to work for a framework and operating system that aren't even used by the general public yet, which is totally understandable.

Here are some technical details about why it's not working. I do think it should work fine with VS11 if you are writing a .NET 4 app, if you are already doing that for whatever reason (can you do that? I actually don't remember). I'm going to try some other AOP tools on Windows 8 and see what I come up with, just in case you desperately need AOP in your hot new Metro app. I'm guessing DynamicProxy probably works in a XAML/C# project, and any JavaScript AOP solutions would probably work in an HTML/JS project, but we'll see...

EDIT:

Chad England has confirmed in a blog post that you can get PostSharp working in VS11 with non-.NET 4.5 projects.

One of my favorite acronyms is "RTRJ" for "Right Tool for the Right Job". At worst, it's something of a cliche, and sometimes even an excuse or blanket answer. But I think it's an important thing to keep in mind whenever you are thinking about architecture of an application. If there was a perfect framework or tool for every job, then architecture would be easy because then every developer would always be on the same page.

I came across this article on CodeProject about making architectural decisions and choosing technologies for a .NET project. While you may not agree with every choice made for that project just from reading, I think it's interesting and important to note why each decision was made. For instance, the author was looking at IoC/AOP tools for the project, and considered many popular tools like Spring.NET, LinFu, and PostSharp. Ultimately, the author determined that Spring.NET was the best choice because of its documentation, integration with NHibernate, and the fact that it also comes with dependency injection capabilities. The author also notes the downsides and risks to using Spring.NET.

I think "Right Tool for the Right Job" consists of two main concepts: understanding the job and choosing the right tools. Everyone can probably rattle off their favorite tools, but are all of those tools correct for this project?

  • Is the rest of the team more familiar with and comfortable with a different tool?
  • Will this project be cheaper or faster to finish with a certain tool?
  • What about support: is an older tool with a bigger support community better than a newer tool that doesn't have a big following yet?
  • Are the benefits of new tool going to outweigh the uphill challenge of learning curves and convincing others to adopt it?
  • Biases: even if you don't admit you have them, you have them. Figure out a good way to make your analysis objective: have others on the team do the spike and get their opinions. Try to play devil's advocate. Find someone to ask you the hard questions and find the answers.
  • Is tool X being used just because it's always been used before? There's probably a good reason for that, but don't be afraid to challenge assumptions and the status quo, even if it does end up being quo.

Above all, don't forget: shipping is a feature. You could spend eternity trying to figure out just the perfect combination of tools and architecture, but ultimately the best way to get feedback is to get building! If you find yourself putting a square peg in a round hole during your first sprint (I'm assuming you are using some sort of agile methodology), say so at your retrospective and switch to something else while you still can.

Someone asked in a comment on my post about OnActionExecuting if one could get access to the model in these filters in order to record some audit data. I think this is probably a good use-case for using PostSharp or Castle DynamicProxy to write an audit aspect and apply it on your service classes or repositories.

But suppose you wanted to record certain fields and other audit information about information being modified in your controller. I wasn't sure if this was going to work, so I coded something up in 30 minutes, and much to my surprise it worked pretty good.

It's too long to embed here, but here's the Gist link to an Audit ActionFilter proof of concept. To use this attribute, simply decorate the actions you want to audit like: [Audit(typeof(SomeEntity), "EntityPropertyName1", "EntityPropertyName2"]. The filter will first check to make sure the incoming entity results in a valid ModelState. If it's not, then no reason to record an audit record because you aren't persisting changes yet (maybe the user missed a required field).

If it is valid, then it will interrogate the parameters and arguments to see if it can find one of type SomeEntity. If it does, it will then interrogate the properties of that entity to get all the values of the properties that are named EntityPropertyName1, EntityPropertyName2, etc. It will then record all this information, along with other audit information like action name, controller name, the current user, timestamp. All this "interrogation" is being done via reflection.

In my example, it just writes it to TempData so that it can be displayed back out. In a real example, it would write it to whatever data store you are using for your audit records (a database, for instance).

Problems with this method of auditing:

  • Reflection - it's slow and brittle. This version is very simple and it's still quite messy. A real production version could be downright unwieldy
  • Line 61 of CustomerController.cs - I'm making the assumption that a ToString on each entity's property is meaningful. That may not always be the case, which could lead to even more complexity.
  • If you spell one of the field names wrong, you could have problems. Refactoring/renaming of your entity properties could break this quite easily.

That being said, if you have a very large system and audit requirements like this, then this messy approach could be better than the even messier alternative of boilerplate audit code in every controller action. I still think using PostSharp or DynamicProxy on the entity repository/service layer would be a cleaner approach.

I contacted Nestor with my proof-of-concept, and he had some comments about possible further refinements:

  • Store the name of the class and properties to be audited in a class/model of its own, so an administrator could decide which properties to audit via some administration tool.
  • Store the property/value pairs audited in a XML format in the model, facilitating the search of some values (what were the activities of a given user, who or when a given  class was deleted o modified, etc.) using the abilities of SQL Server in making indexes over XML columns.
Matthew D. Groves

About the Author

Matthew D. Groves lives in Central Ohio. He works remotely, loves to code, and is a Microsoft MVP.

Latest Comments

Twitter