Skip to main content

Posts tagged with 'IL'

Sorry for another slow week of posts--I've been busy on a couple of side projects (neither of which involve AOP), so my evening writing time has been cut a little short. Here's your weekly dose of AOP links:

That's it for this week--keep sending in those links for next time.

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.

One of the problems with object-oriented programming that AOP aims to solve is excessive boilerplate. And there's no more stark example of excessive boilerplate than the use of INotifyPropertyChanged.

NotifyPropertyWeaver is a tool that adds an MSBuild task to your project. It will find all your implementations of INotifyPropertyChanged and rewrite the properties to do all the notification code for you. So you don't have to worry about mistyping that property name string, and you don't have to worry about refactoring when you rename or remove properties.

In this code example below, the first file is a typical implementation of INotifyPropertyChanged, done entirely by hand. Notice all the noise and duplication: explicit backing properties, multiple calls to NotifyPropertyChanged to account for the derived property, the use of a string that exactly matches the property names. And this is a class with only three properties. Imagine if you had a whole bunch more properties, and imagine the maintenance problem to add/remove/change one of the derived properties!

The second file is an example of how you would do the exact same thing using NotifyPropertyWeaver. You don't have to write the NotifyPropertyChanged method, or call it in the setters, or write backing properties. This class is so much easier to read and maintain. The third file is actually a look at the class after it's been compiled, run through NotifyPropertyWeaver, and then disassembled with Telerik's JustDecompile. Look familiar? It's almost exactly the same as the hand-coded implementation.

And finally, the fourth file is the change that will be made to your project file when you add NotifyPropertyWeaver with NuGet. It calls a task that uses Mono.Cecil to rewrite the code for you. You don't need to reference the build task DLL in your project.

NotifyPropertyWeaver is a tool that does a single task, but this approach (an MSBuild post-compiler task) can be used to implement other AOP features. Fody is a tool by the creator of NotifyPropertyWeaver to do just that.

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.

OnActionExecuted method in an action filter will be run after the controller action it is filtering is executed. The context object that gets passed in of type ActionExecutedContext, which is very similar to ActionExecutingContext in the types of information you can get with these exceptions:

  • There is no ActionParameters dictionary property.
  • You will get some information related to an unhandled exception:
    • Exception - the exception that was thrown by the action
    • ExceptionHandled - set this to true if you've handled the exception in OnActionExecuted (otherwise it will be bubbled up)
    • Canceled - this can get a bit confusing, but if the process was canceled by setting a different result in OnActionExecuting this will be set to 'true', or you can set this to 'true' yourself to cancel further processing. For more info, see this post on how MVC handles filters and this discussion on the Canceled property.

I did another "kitchen sink" view of an ActionExecutedContext object:

Paired with a slightly different View, which results in:

Kitchen sink of ActionExceutedContext

Note that one of the most interesting things you can do is to change the result of the action (like I did to make the Kitchen Sink example possible). For instance, if you wanted to redirect to another action, show a different view, etc,

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