AOP in the ASP.NET MVC way
August 6, 2008
DISCLAIMER. The following post applies to the Microsoft ASP.NET MVC Extensions (Preview 4) that have been published on July 16th to the ASP.NET Community Web Site at Codeplex.
Last week I was discussing with Matías about the implementation of DFO (Design For Operations) for a system we are building at Southworks. Our discussion took us to an Ayende’s post that talks about Logging The AOP Way for .NET. At that moment we thought that’s what we want.
Later on, reading ScottGu’s blog, we realized that a filtering pipeline has been built for the ASP.NET MVC, so we thought about mixing what ScottGu’s blog says to achieve what Oren’s was posting on Logging the AOP Way.
I have divided this post in two parts, the first is the conceptual part where I’m going to discuss a little bit about the Filtering model of ASP.NET MVC and then I’ll show a little how-to build a custom tracing filter.
What are ASP.NET MVC Filter Interceptors?
As described in ScottGu’s post Action Filter Attributes are declarative way to inject code that can be executed pre and post controller’s action execution. Also the same can be done for pre and post ActionResult conversion into an ASP.NET response. This enables another way to do AOP and achieve for example the logging described by Oren’s at his post.
How do they work?
In order to understand how do they work let’s analyze how does the ASP.NET MVC pipeline goes. Below is a diagram that demonstrates how does an action flows from the HttpRequest until the ActionResult gets executed.
Now, let’s go one by one the items diagrams and I’ll try to explain what’s happening on each part:
- The user makes a call using the browser.
- Given the URI of the request the ASP.NET MVC Handlers looks for the appropriated controller using the Routes configured on the Global.asax. Prior instantiating the controller a ControllerContext is created. It wraps ,among other things, the current HttpContext for a given request execution.
- The ASP.NET MVC engine, instantiates the appropriated controller passing the current context as a parameter.
- The controller executes the action that correspond to the current Route.
- As a result of the Action execution an ActionResult is returned to the context, given the ActionResult implementation type (must inherit from ActionResult) it might be turned into a View (ASPX Page) or JSON as an example.
Now that the baseline execution context is a little bit dissected let’s explain where does the ActionFilter attributes hook to this model.
The items squared with black on the diagram above are the hooks that can be used to inject the code. Those are the interaction points where we can create our hooks and perform crosscutting operations like logging, tracing, error handling and even authZ and authN.
- OnActionExecuting. The ActionExecuting contains all the data about the action that is going to be executed prior its execution.
- OnActionExecuted. The ActionExecuted contains all the data gathered after the action execution.
- OnResultExecuting. The ResultExecuting contains all the data about the ActionResult that is going to be executed. Two of the differences that you will find between this and the previous hook are:
- If the Action execution ends in an exception you might not reach this point.
- If the user called for example "View()" which renders the default view for that action, in the previous one you don’t know the view name.
- OnResultExecuted. The ResultExecuted contains all the data gathered while the ActionResult was turning into an ASP.NET Response.
Up to now we understand how does this ActionFilter pipeline works and which data we can get from it. Now, you might be wondering how do you get this in your code. It’s just simple as decorating your Controller and/or Action with the desired Action Filter attribute.
[TraceFilter] public ActionResult Index() { ViewData["Title"] = "Home Page"; ViewData["Message"] = "Welcome to ASP.NET MVC!"; return View(); }
In the example above I placed a custom attribute called TraceFilterAttribute that is a custom Filter I have created for demo purposes. Wondering how? Read below!
Do it yourself, your own ActionFilterAttribute in 5 minutes.
In this section we will discuss how to create a custom attribute. This attribute will trace the calls to the controller and log them like:
The log entry above displays the Date Time the entry happend, the warn level [Debug || Fatal || Error || Information] and the parameters if they were any.
Step by Step sample
- Open Visual Studio (ASP.NET MVC works on Web Express versions of VS2008).
- Create a new ASP.NET MVC Web Application and name it as you want.
- Using the Solution Explorer, create a folder on web site’s root and name it Filters.

(Figure 1. - Filters folder on Solution Explorer) - Add a new Class file and name it TraceFilterAttribute.
- Delete the TraceFilterAttribute class declaration from the file.
- Inside the namespace declaration, use the attribute code snippet to create the attribute structure.
(Figure 2. Using the Attribute code snippet).
- Name your attribute (using the snippet) TraceFilterAttribute and make it inherit from the ActionFilterAttribute.
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] public sealed class TraceFilterAttribute : ActionFilterAttribute { }
- Copy and paste the code below inside your class declaration
public override void OnActionExecuting(ActionExecutingContext filterContext) { StringBuilder builder = new StringBuilder(); filterContext.ActionParameters.ToList() .ForEach(a => builder.AppendFormat("{0}:{1}; ", a.Key, a.Value ?? "{empty}")); var parameters = builder.ToString(); var message = string.Format(CultureInfo.InvariantCulture, "{0} {1}.{2} => {3}", filterContext.ActionMethod.ReturnType, filterContext.Controller.GetType().Name, filterContext.ActionMethod.ToString().Replace(filterContext.ActionMethod.ReturnType.ToString(), string.Empty) .Trim(), string.IsNullOrEmpty(parameters) ? "(void)" : parameters); this.logger.Debug(message); }
- The Logger used above is using Log4Net, which provides and easy and configurable way to do logging.
- In order to configure the logger (once you have added the reference to Log4Net) copy and paste the following to your section declaration in your web.config.
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" requirePermission="false"/>
- Now include this other chunk of XML before the </configuration> tag.
<log4net> <!– The DebugFileAppender writes all messages to a log file–> <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="logfile.txt" /> <appendToFile value="true" /> <rollingStyle value="Size" /> <maxSizeRollBackups value="-1" /> <maximumFileSize value="50GB" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="(%date) [%-5level] %message%newline" /> </layout> </appender> <root> <!– add other appenders here and the log messages will be sent to every listed appender –> <appender-ref ref="RollingLogFileAppender" /> </root> </log4net>
- In order to consume the logger using Log4Net copy and paste the following code inside your class declaration
private ILog logger; public TraceFilterAttribute() { XmlConfigurator.Configure(); this.logger = LogManager.GetLogger(typeof(TraceFilterAttribute)); }
- Now that we have built the attribute, let’s apply it to the Index() method on the HomeController.cs . Your code should look like this:
[TraceFilter] public ActionResult Index() { ViewData["Title"] = "Home Page"; ViewData["Message"] = "Welcome to ASP.NET MVC!"; return View(); }
- Press F5 and you will see the default MVC home page.
- Now go the web application project directory and look for the logfile.txt.
- Open the logfile.txt, it should look like this
- That’s it , you’ve built your own Action Filter Attribute.
One more little trick
You might want to turn this sample into something for real, there’s a trick that will enable you do the tracing just if debug is enabled. All that you have to do is surround the OnActionExecuting method body with the code depicted below.
if (filterContext.HttpContext.IsDebuggingEnabled) { // The method body goes here. }
Now, your attribute will log data just if the Debug mode is enabled on the web.config of your application.
<compilation debug="true">
Conclusion
We’ve learnt how to develop a custom Action Filter Attribute. This is a very powerful and cool feature of ASP.NET MVC. This enables you to do logging, tracing and exception handling. I love this approach of doing AOP with Interceptors and the most that I like that is built in to the technology. There’s unlimited power on this, but remember a great power also means a great responsibility.
thanks,
~johnny
Just released: LitwareHR - November 2007
November 19, 2007
I’m proud to announce that we have helped Microsoft Architecture Strategy Team shipping another version of LitwareHR. These are (among others) the enhancements done for this new version of LitwareHR:
· Platform upgrade. Updated LitwareHR v2.0 to run on Windows Sever 2008 and Microsoft Visual Studio 2008 (Beta 2). This included leveraging new technologies like Active Directory Lightweight Directory Services (ADSLDS), new management APIs and IIS7.
· Performance. We did performance assessment work for Multi Tenant Database design, the outcome of this research is reflected on a new asset called Multi Tenant Database Guide. Based on this assessment we have updated LitwareHR data access code to enhance its performance.
· Services Enhancements. Created new services leveraging Windows Communication Foundation 3.5 features as RSS & REST.
· SilverLight Streaming mash-up. We did a mash-up with LitwareHR and Silverlight Streaming Services that allows candidate evaluation screen to display a video submitted by the applicant.
· Automated Deployment. Moved from the old document + readme + bunch of scripts approach to a new one including dependency checking and automated configuration.
· Smart Client Application. We included a WPF application that shows how to consume LitwareHR services from an external application. Also shows offline capabilities.
Gadget. We extended the UX of Litware up to the candidate desktop with two gadgets: one for Fabrikam and one for Contoso.
So now go and grab the bits:
http://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=LitwareHR&ReleaseId=8439
thanks,
~johnny
Conference Day at Santiago del Estero
October 31, 2007
Hi Everybody, after the Code Camp @ Buenos Aires, I’ve packaged my stuff and came to Santiago del Estero. Here the Santiago del Estero National University (UNSE) is holding a technology conference for 3 days (29, 30, and 31 of October). This conference is called JUITECE (Jornadas Universitarias de Informática, Tecnologia, Electronica y Ciencias de la Educación).
I started my day at the Buenos Aires airport where I spent 3 hours more than expected since my flight was delayed. At 12:30pm I flew to Santiago del Estereo and I’ve arrived at 2:30pm.
Once I was completed settled, I went to the university where I was anxious to see the conference about Virtualization my friend, Mario Montalvetti, was presenting. The conference was great, he started from the history of Virtualization and then moved across all the types of virtualization that exist and also the upcoming products (like Microsoft Softgrid).
After Mario’s track, it was time for me to present Microsoft PopFly. The conference went very good, I started describing the Web 2.0 Ecosystem and then the mashups that you can create using Microsoft PopFly.
Also this journey was another great opportunity to spend some time with those guys passionate about technology and help them on User Groups creation. I’m very happy with the commitment, and passion these people have on their personal/group development.
After visiting the university I had time to sightsee Santiago del Estero city which is getting nicer day by day. I took some pictures, and have a cool dinner with the people here. We had problem, an unexpected storm left the city without lights for a while and the streets underwater. I almost had to swim to back get to the hotel.
Now it’s time to go back, I’m preparing my stuff to fly back to Buenos Aires. My plane is leaving in a couple of hours. Hope to come back soon.
Thanks,
~johnny
Memories from Code Camp - Buenos Aires 2007
October 28, 2007
On October 24t we held the Microsoft Code Camp - Buenos Aires 2007. The event was really good, there were lots of people and of course the speakers were great.
Code Camp was a great opportunity for students to get involved on our daily work, during the event they could see the work done by our user groups, two of the best industry speakers (Alejandro G. Jack and Andrés Aguiar), a WPF Guru like Paulo Arancibia, and also had time to win an X-Box 360°.
I’m really happy with our first Code Camp, I think it’s the first of lots of events where UG and MSPs will be able to expose what they do and what they’ve been working on.
I hope all the assistants have left with a sensation that for the next Code Camp they can be on the other side (as presenters) since this event was thought by students for students.
About my track, you can get DinnerNow source code from http://www.dinnernow.net and start playing around with the bits.
Finally, I want to thank all the people that made this event possible: Southworks Team, Microsoft Argentina Faculty Contact Center, ASPSOFT, Andrés Aguiar (Infragistics), All the MSP (Microsoft Student Partners), and of course to my Track partner Augusto Alvarez.
And remember….
Technology is a journey… enjoy the ride
Thanks,
~johnny
Code Camp 2007 - Buenos Aires
October 6, 2007
We’re getting closer to the Microsoft Argentina Code Camp to be held on Oct. 24th at UADE (Universidad Argentina de la Empresa).
This event is *completely free* and it will be the coolest event this year targeted for Students considering the topics to be discussed and the involved technologies . We’ll speak about: XNA (games development), Silverlight, WPF, Robotics, Software Architecture among others.
We will show the most interesting tools and technologies shown by Students for Students. The event won’t be pure theory, most of our content is designed to be interactive demos. Also we’re going to provide resources trying to encourage students to get involved and up on speed faster than they think.
You can go now and register at: http://www.microsoft.com/argentina/codecamp/
Agenda
Meeting the speakers
Behind: Pablo Michelis - MS Argentina. Bottom from RtoL: Damián Galletini and me
Behind: Pablo Michelis - MS Argentina. Bottom from RtoL: Damián Galletini and me
thanks,
~johnny
Repository Factory - Week #3
August 28, 2007
As I promised when we launch this project, I’m reporting each week the progress that we’ve accomplished in the past week. This week has been mostly QA and a highly valuable improvement.
Improved wizard reentrancy
Last week we’ve accomplished wizard reentrancy for Create repository recipe, but we had a bug where we’re not persisting the Identity for each operation (when it applies). Now we’ve added the Identity to the recipe state file as depicted below.
Fixed mapping duplication
When you had a stored procedure that uses a parameter and it’s also included on the as ReturnValue it appear twice in the mappings. Now we’ve fixed this by adding the parameter direction on the recipe state file. How now a parameter looks like
<parameter propertyName=”Name” parameterName=”pty_Name” parameterDirection=”6″ />
Enabled recipes to run on IIS hosted project
We’ve done a small fix to the GetConfigurationAction that allows you to run the recipes that uses configuration file on web sites that are not on File-System.
Place generated code on folders by entity
This was the major improvement for this week. Previously when you generated a repository, all the files that comes along with it where added to the root of the Data Access Project. Chris figured out that once we’ve the RepositoryFactory.Create you no longer needed to be aware of those file and generated repository implementation neither. Now this is how your data access project will look like.
This improvement has a couple of benefits:
- If you want to get rid of the generated code and maintain just the interface, now you just have to remove the {EntityName}RepositoryArtifacts folder.
- All the generated code is encapsulated in a single folder, so you don’t have to be aware of how many files and the implementation details since you’re relying on the factory.
- In addition to the RepositoryFactory.Create() this will keep your projects not aware of where the generated files are.
Finally, if you use the generated stuff as showed below, you’ll just have to include a Using to DataAccessProjectNamespace.
using ClassLibrary17; using Microsoft.Practices.Repository; public static class Class1 { public static void Main() { IPersonRepository repository = RepositoryFactory.Create(); } }
This week we’re probably tackling the rework of the UI. We want to make the factory more usable, since we consider the usability as a core part for software factories.
Summary
We finished another week, all the tackled issues were not invented by us, they are community requests. We’re listening to you, please feel free to provide feedback in our community site. We work on the improvements starting with highly ranked ones, you have a chance to influence the course of the project.
thanks,
~johnny
Repository Factory support in EntLib Configuration Tool
August 24, 2007
Reading David Hayden’s blog, I’ve found that he made a plug-in that allows you to manage Repository Factory configuration information in your app.config/web.config file using the Enterprise Library Configuration Tool GUI.
You can read more on the plug-in on his post here.
Stay tuned, another update on Repository Factory is on its way.
thanks,
~johnny
Repository Factory - Week #2
August 20, 2007
Another report of Repository Factory from the trenches. Now we’ve completed the week of work. In addition to what we’ve done last week this is the report of the stuff completed during this second week.
As I told you on previous posts this project is community oriented, you can decide what we should do during next week. How? Enter our community site and keep voting for your favorite issues.
Repository Class improvements
Our previously generated repository used to have the databaseName field on its declaration, that field is no needed since it’s declared on base class (Repository). In addition we’ve updated the Repository class to allow you instantiate a repository using the default database declared on the config file.
public class PersonRepository : Repository, IPersonRepository { // This field has been removed.private string databaseName;public PersonRepository(string databaseName) : base(databaseName) {} // Added c’tor overload that allows you the usage of defaultDatabase // declaration used by Enterprise Library DAAB. public PersonRepository() : base() {} }
Enterprise Library default database configuration, it’s done by adding this line to your config file.
<dataConfiguration defaultDatabase=”myConnectionString” />
In addition to the changes done to the Repository class and T4 template, we’ve modified the RepositoryFactory class allowing you to instantiate a repository without connection name.
public static class RepositoryFactory { // These are the added overloads that allows you // the instantiation of the repository class without // passing the connection string name. public static T Create(); public static object Create(Type repositoryInterface); }
Combining that configuration line and the improvements done this week, now you’re allowed to use the RepositoryFactory in this way
public static void Main(string[] args) { IPersonRepository repository = RepositoryFactory.Create(); }
Configuration issue fixed
We’ve modified the way the recipe edits the configuration file and appends the repository mapping information. More information can be found in the following snippet I took out from the code.
[FILE: SetRepositoryMappingAction.cs]
public override void Execute() { // We’re using XML in this action because there’s a // dll issue with Guidance Package and the target project. // They’re referencing the same assembly but in different // locations and that generates that runtime errors. XmlDocument document = new XmlDocument(); document.Load(configuration.FilePath); EnsureConfigurationSectionDeclarationExists(“repositoryFactory”, Resources.RepositoryFactorySectionDeclaration); XmlNode repositoryFactorySection = GetOrCreateConfigurationSection (“repositoryFactory”); AddRepositoryMappings(repositoryFactorySection, document); document.Save(configuration.FilePath); }
Create Repository wizard reentrancy
One of the most voted issues on our community site was giving the “Create repository recipe” the ability to store the operations and mappings selected the last time you’ve used it to create a repository for a given entity. I’ll show this new improvement with a sample.
The wizard page
On this page you’ve to specify the operations you want to generate in your repository class, the improvement we’ve done allows you to store this information as metadata on a rcpState file.
The generated recipe state file
The recipe state file is an Xml file that stores the information of the operations you’ve chosen to generate the repository. The structure is depicted on the snippet bellow
<repositoryInformation> <repositoryMetadata name=”Person”> <operation operationName=”DeletePerson” hasInputParameters=”True” procedure=”DeletePerson” operationType=”DeleteOne” /> </repositoryMetadata> </repositoryInformation>
Finally the Xml is stored on the solution folder, with the solution name and rcpstate extension.
Documentation
We have added the ///XMLDoc to all of the types existing on the Microsoft.Practices.Repository.
Summary
We finished another week, all the tackled issues were not invented by us, they are community requests. We’re listening to you, please feel free to provide feedback in our community site. We work on the improvements starting with highly ranked ones, you have a chance to influence the course of the project.
Also we’ve dropped our first CTP that you can find as source code release here.
thanks,
~johnny
Repository Factory - Week#1
August 13, 2007
We've completed the first week working on the Repository Factory project. As I mentioned on a previous post, Chris Tavares and I are working on this amazing project.
I'd like to share with you the improvements we've done during this week, and remember that your vote decides the future of the project. Enter codeplex and keep voting, we're tackling most voted stuff.
GAT/GAX July 2007 Support
We have migrated the guidance package to GAT/GAX July 2007 CTP. More about GAT/GAX 2007 migration can be found here.
Service Factory independent
We've removed all the dependencies to Microsoft Patterns & Practices Service Factory, so you can run the package without the necessity of having the Service Factory installed.
Place generic code into its own DLL
As you might recall the formerly Data Access Guidance Package generated a folder called generic on your Data Access Project. The intent of that was having base-classes for the repository assets. But that code was always the same, so we moved that code a new assembly called Microsoft.Practices.Repository. This new assembly contains the generic code, making your DAC projects are smaller, and easy to maintain.
Generate interface for Repository
Let's explain this point with little bit of code. Suppose that we've a presenter class, that uses the repository, and we want to use IoC pattern. Our code should look like this:
public class Presenter { public Presenter(Repository repository) { //TODO: Add some constructor logic here } }
Since we're sticking to the concrete repository implementation, we won't be able to mock the repository implementation, when we want to write a unit test. Ok, you might be thinking that you can easily do Right Click -> Extract Interface, we knew that but better than using the refactoring tools we're doing it for you. Both repository and interface are generated by Repository Factory. e.g.
//The interface generated by RF public interface IMyRepository { void Add(MyEntity entity); } //The concrete class public class Repository : IMyRepository { public void Add(MyEntity entity) { } }
Create factory to retrieve repositories
Based on the previously explained improvement (Generate Interface for Repository), we've created a RepositoryFactory (besides the GP, a class) that you can use on your code to insulate callers from the concrete repository implementation. Based on this you can write code to retrieve the repository by it's interface and then you can change the implementation by modifying the config file.
Usage
public void SomeMethod() { IPersonRepository repository = RepositoryFactory.Create(connectionString); }
On the config file you'll have something like this
<repositoryFactory> <repositories> <add repositoryInterface=”IMyRepository, MyAssembly” repositoryType=”MyRepository, MyAssembly” /> </repositories> </repositoryFactory>
Auto-map entity fields to Stored Procedure parameters
Sometimes you'll have to add an operation to your repository that is more than just a single CRUD operation. Previously when you needed to create this operation you had to map the input/output parameters and return values from to entity fields. Now we've improved this by automating the mapping. It's a little bit smarter than just mapping Name -> sProcName, it performs the mapping ignoring case and removing “pty_” (and other suffixes).
Summary
We finished this first week, all the tackled issues were not invented by us, they are community requests. We're listening to you, please feel free to provide feedback in our community site. We work on the improvements starting with highly ranked ones, you have a chance to influence the course of the project.
We're going to be delivering a CTP version soon! So stay tuned…
thanks,
~johnny
PS. Acknowledgements
I want to thank Paulo who gave my blog the new head-wash, he is a really creative person, don't forget to check out his blog…
DinnerNow for Microsoft Visual Studio Beta 2
August 9, 2007
Yes, we're back with another release! You might noticed that people is talking about Orcas Beta 2, but you will be wondering “How can I see all the Microsoft Visual Studio 2008 features running and working in the same place?”. Here's your answer DinnerNow is back!
We built this version of DinnerNow using Microsoft Visual Studio 2008. We've modified the code to be Microsoft Visual Studio 2008 compliant.
Highlights of the drop
- Official Windows CardSpace Icons
- Revised WPF kiosk application that can be loaded in the Visual Studio designer for WPF
- Integration with the mobile application
- Migrated Service Portfolio projects to new templates (WCF Service Library & Workflow Service Library)
- Improved the REST/POX/RSS support using the new WCF 3.5 programming model.
So what? Get the bits now!
thanks,
~johnny












