DomainAspects: Aspect Oriented Programming with Castle Windsor Interceptor

Last week I released DomainAspects 2.0 without many details other than its general features. In this post and future posts, I’m going to delve into the details one area or feature at a time. My purpose is to help you understand how each piece works and provide examples of how you might use the library or the code in your own projects.

The few aspect oriented programming (AOP) languages and frameworks that I have looked at seem to me to be unwieldy and unnecessarily intrusive in the the daily workflow of an application developer, cluttering up the code and creating cumbersome dependencies on the AOP construct itself. Fortunately there is a cleaner alternative to AOP languages found in the inversion of control (IoC) containers we so commonly use for dependency injection in order to enjoy the benefits of SOLID Design: the Interceptor.

Interception is supported by many of the IoC libraries. I’ve used Unity a little but most of my experience is with Castle Windsor, so of course, I prefer Windsor. I also prefer Windsor’s interception approach because I find it easier. If you are a Unity user, take a look at Dino Esposito’s Unity Interception article on MSDN. You an see for yourself that the Unity IInterceptionBehavior implementation is relatively more complex than Windsor’s IInteceptor:

public interface IInterceptor
{
   void Intercept(IInvocation invocation);
}

In DomainAspects, I wanted my Interceptor to handle logging and exception handling, which I like to call “auditing”, probably the most common of the cross cutting concerns handled by AOP. I also wanted the Interceptor to handle authorization security. But I wanted to have a simple Interceptor class that would have its “auditor” and “authorization” dependencies injected by the same container. Each dependency would handle its specific responsibilities.

Here’s the Windsor fluent configuration code for each of these. Note the first component is the IPrincipalProvider which is a dependency of the IOperationAuthorizer implementation. I then register the components for the authorizer and the auditor. The last component configured by the DomainAspects ServiceFactory class is the OperationInterceptor.

_container.Register(
   Component.For<IPrincipalProvider>()
      .ImplementedBy(_principalProviderType)
      .Named("PrincipalProvider")
      .LifeStyle.Transient,

   Component.For<IOperationAuthorizer>()
   .ImplementedBy(_operationAuthorizerType)
   .Named("OperationAuthorizer")
   .LifeStyle.Transient,

   Component.For<IOperationAuditor>()
   .ImplementedBy(_operationAuditorType)
   .Named("OperationAuditor")
   .LifeStyle.Transient,

   Component.For<OperationInterceptor>()
   .Named("OperationInterceptor")
   .LifeStyle.Transient);

When the DomainAspects ServiceFactory’s Create method is called, if the service has not already been registered with the Windsor container, it will register the container and wrap it with the Interceptor we have just configured using Windsor’s fluent interface. Here’s the code:

_container.Register(
   Component.For(interfaceType)
   .ImplementedBy(implementedByType)
   .Named(implementedByType.Name)
   .Interceptors(InterceptorReference.ForKey("OperationInterceptor")).Anywhere
   .LifeStyle.PerThread);

Note that the .Interceptors method takes a params of InterceptorReference objects. So like Unity, you can chain interception. Unlike Unity, the chaining occurs automatically for you in sequence of the array. Your IInterceptor implementation does not require you to be concerned with wiring up the chain as does the Unity IInterceptorBehavior.

Now take a look at the relatively simple, application requirement focused OperationInterceptor class which implements the Windsor IInterceptor interface with a single method and depends on the container to inject its two dependencies. The code in the interceptor class here just handles the business case of examining the class and method being intercepted for attributes that will require action by one or both of its dependencies.

public class OperationInterceptor : IInterceptor   
{
   private IOperationAuditor auditor;
   private IOperationAuthorizer authorizer;

   public OperationInterceptor(IOperationAuditor opAuditor, IOperationAuthorizer opAuthorizer)
   {
      auditor = opAuditor;
      authorizer = opAuthorizer;
   }

   public void Intercept(IInvocation invocation)
   {
      AutoLogAttribute autoLogAttribute = null;
      AuthorizeAttribute authorizeAttribute = null;
      var methodInfo = invocation.MethodInvocationTarget;
      object invokedKey = null;
      try
      {
         //find AutoLogAttribute
         autoLogAttribute = ((AutoLogAttribute[])methodInfo
            .GetCustomAttributes(typeof(AutoLogAttribute), true))
            .FirstOrDefault();

         if (null == autoLogAttribute) //try class level
            autoLogAttribute = ((AutoLogAttribute[])methodInfo.DeclaringType
            .GetCustomAttributes(typeof(AutoLogAttribute), true))
            .FirstOrDefault();

         if (null != autoLogAttribute &&  null != auditor)
         {
            //log invocation
            auditor.LogOperationInvoked(methodInfo, 
               invocation.Arguments, 
               invocation.GenericArguments, 
               out invokedKey);
         }

         //find AuthorizeAttribute
         authorizeAttribute = ((AuthorizeAttribute[])methodInfo
            .GetCustomAttributes(typeof(AuthorizeAttribute), true))
            .FirstOrDefault();

         if (null == autoLogAttribute) //try class level
            authorizeAttribute = ((AuthorizeAttribute[])methodInfo.DeclaringType
            .GetCustomAttributes(typeof(AuthorizeAttribute), true))
            .FirstOrDefault();

         if (null != authorizeAttribute && null != authorizer)
         {
            //authorize
            authorizer.Authorize(methodInfo, invocation.Arguments, invocation.GenericArguments);
         }

         //proceed with invocation
         invocation.Proceed();
      }
      catch(Exception e)
      {
         //log exception and throw
         if (null != autoLogAttribute && null != auditor)
         {
            auditor.LogOperationException(invokedKey, methodInfo, e);
         }
         throw; //always throw to bubble up exception and preserve call stack
      }
   }
}

If you are considering aspect oriented programming or you need interception for another reason, I hope this post is helpful. If you are looking through the DomainAspects 2.0 library code, I hope this will help you understand how and why I’ve used Windsor’s IoC container and their interception facility.

Learning from the Elevator State Machine Programming Test

In a recent job interview I was asked to build an elevator simulator. I was given the weekend to do it. I had never written a state machine before. It wasn't perfect but it passed all the tests I could come up with based on my own assumptions about the rules of behavior based on the following text from the interviewer:

Build an elevator simulator. It should have a UI that shows 5 floors, with the call buttons for each floor. The UI should also show the panel of buttons inside the elevator. As a user, I can press any floor call button (up or down), as well as elevator panel buttons to move the elevator. The elevator should move at a realistic speed (not just go instantaneously to the desired floor).

Evaluation criteria:

  1. Accuracy of the elevator state machine with various button-press scenarios.
  2. The object design of your project – how you organize the design into classes and their relationships, what design patterns you choose to use.
  3. Coding style and clarity.
  4. The UI doesn’t need to be fancy, but it needs to enable the functional requirements stated above. You can use any UI toolkit of your choice.
  5. The code should be written in C#.

Unfortunately, my assumptions were not the same as the evaluation team's assumptions, so they found some "bugs" in the behavior of my elevator. Honestly, I was not surprised. In fact, when I submitted the code, I had offered the following observations:

I did not spend much time refactoring or documenting, so you're getting to see the "first draft" of the code.

Some refactoring that I would do:

1. Create tighter assurances in the tests with required sequence of logged events rather. In running the tests, I took a shortcut and debugged through the tests to review the log entries from the state machine to verify the sequence of actions. This should be corrected.

2. Clean up each of the two state machines, AtFloor and Moving. To simplify, I might create one or two more state machine classes to make the logic rules easier to follow and remove some of the "if" fall through logic states.

3. Tighten up UI with view data model to reduce the clutter in the code or perhaps even rewrite the UI in WPF with an MVVM architecture. My choice of Windows Forms was based on time limitations and my limited experience with WPF.

4. Review and tighten up naming and coding conventions for improved readability.

The "bug" in the elevator was not the reason I did not get the job. Fortunately, the recruiter sent me the feedback she received from the interview team. This is rare. I found it helpful and will certainly take it into account in the future.

The positive and exciting thing about this entire experience was having a reason to write my first state machine in a fun exercise which I want to share with you here. This was certainly the most comprehensive programming test I've ever been given in conjunction with an interview. I applaud the employer for having such a thorough and extensive evaluation process. And I thank the interview team for their feedback. Such thoroughness really helps to determine whether a person will be a good fit for the team. And above all else, that fit is of paramount importance.

Please download the code and let me know what you think. There are clearly flaws, as I would expect of any code thrown together over a weekend, especially where the particular pattern being implemented is a first attempt. When I have time, I may want to pull this off the shelf and do the refactoring I suggested when I submitted the code. But first, I need to find an employer and settle down. I think that will happen soon.

ElevatorControl.zip (29.94 kb)

Moved from dasBlog to BlogEngine.NET

I bit the bullet today and moved from dasBlog to BlogEngine.NET after having upgraded my hosting account to .NET 4.0 and having way too many problems with my dasBlog install. I just exported the dasBlog content to BlogML.There are multiple posts about doing this.

The only thing I changed was the Global.asax code to create a permanent redirect for my dasBlog links out in the world to the BlogEngine.NET links using the following:

void Application_BeginRequest(object source, EventArgs e)
{
   HttpApplication app = (HttpApplication)source;
   HttpContext context = app.Context;
    
   // Attempt to perform first request initialization
   FirstRequestInitialization.Initialize(context);

   //redirect to ~/post/2012/03/30/DomainAspects+Inject.aspx
   //when url is ~/2012/03/30/DomainAspects+Inject.aspx
   if (context.Request.Url.AbsolutePath.EndsWith(".aspx"))
   {
      Match m = dateRegex.Match(context.Request.Url.AbsolutePath);
      if (m.Success)
      {
         string url = context.Request.Url.AbsolutePath;
         string dt = m.ToString();
         url = url.Replace(dt, @"/post" + dt);
         context.Response.RedirectPermanent(url, true);
      }
   }
}

static Regex dateRegex = new Regex(@"(?<!/post)/\d{4}/\d{1,2}/\d{1,2}/");

SQLite and Fluent NHibernate with “Code First” in C#: Rewriting the OpenCollective Wiki

Introduction
Seven years ago, I wrote a wiki for managing requirements for multiple software development projects. I used C# in a simplistic ASP.NET web forms application with SQL Server as my data store. I published it under the name OpenCollective on Code Project and since then it has been viewed on just over 196,000 occasions and downloaded a little more than 1,600 times.

I still get an occasional question about OpenCollective, so a month or two ago I decided to rewrite it in order to explore using SQLite and Fluent NHibernate in a “code first” approach set in an ASP.NET MVC 3 application written in C#. The original OpenCollective was in some ways an experiment with technologies and techniques I was interested in at that time, so a rewrite to learn and explore something new seemed like a good idea.

The plan is simple. As time allows, I’ll rewrite OpenCollective and blog about my progress whenever it seems I have something of value to share. With some of these posts, I’ll share the entire code base for download. Others may just illustrate something I’ve learned or something I want to preserve for my own future reference. I tackled the first chore over a month ago but work and other activities pushed this project aside for a while, so I’m just getting around to blogging about the code now. I suspect this will be a priority driven pattern for this project.

Code First with Fluent NHibernate and SQLite
The first thing I wanted to accomplish in the OpenCollective rewrite was a move to SQLite and Fluent NHibernate in a “code first” approach to replace the SQL Server schema and the old ADO.NET code in the original. Some quick searching led me to the Fluent NHibernate Getting Started page. (If you haven’t read it, do so now.) Combining that with what I have already learned in exploring SQLite in this blog, I soon had a code first database being generated.

I’ll attach the entire code in 7zip (saves me 25% in bandwidth over standard zip), so I’m not going to go through all of the code in text here, but I do want to share with you some of the key steps in the my code first with Fluent NHibernate and SQLite adventure.

First, create your POCO entity class:

public class Project
{
	public virtual long Id { get; private set; }
	public virtual string Name { get; set; }
	public virtual DateTime Updated { get; set; }

	public virtual Author Author { get; set; }
	public virtual IList<Topic> Topics { get; set; }
	public virtual IList<Attachment> Attachments { get; set; }

	public Project()
	{
		Topics = new List<Topic>();
		Attachments = new List<Attachment>();
	}

	public virtual void AddTopic(Topic topic)
	{
		topic.Project = this;
		Topics.Add(topic);
	}

	public virtual void AddAttachment(Attachment attachment)
	{
		attachment.Project = this;
		Attachments.Add(attachment);
	}
}

Second, create a Fluent NHibernate mapping class:

public class ProjectMap : ClassMap<Project>
{
	public ProjectMap()
	{
		Id(x => x.Id);
		Map(x => x.Name);
		Map(x => x.Updated);
		References(x => x.Author);

		HasMany(x => x.Attachments)
			.Cascade.All();

		HasMany(x => x.Topics)
			.Cascade.All();
	}
}

Third, create a factory class that will return the NHibernate ISessionFactory and generate the SQLite schema and database file:

internal class DataFactory
{
	string _dbFile = string.Empty;
	bool _overwriteExisting = false;

	public DataFactory(string dbFile, bool overwriteExisting)
	{
		_dbFile = dbFile;
		_overwriteExisting = overwriteExisting;
	}

	public ISessionFactory CreateSessionFactory()
	{
		return Fluently.Configure()
			.Database(
				SQLiteConfiguration.Standard
					.UsingFile(_dbFile)
			)
			.Mappings(m => m.FluentMappings.AddFromAssemblyOf<DataFactory>())
			.ExposeConfiguration(BuildSchema)
			.BuildSessionFactory();
	}

	private void BuildSchema(Configuration config)
	{
		if (_overwriteExisting)
		{
			if (File.Exists(_dbFile)) File.Delete(_dbFile);

			var se = new SchemaExport(config);
			se.Create(false, true);
		}
	}
}

Fourth, create a repository class that will let you create the database and perform CRUD operations using your entity classes:

public static class OcDataRepository
{
	public static void Create(string dbFile)
	{
		DataFactory df = new DataFactory(dbFile, true);
		using (var sf = df.CreateSessionFactory())
		{
			sf.Close();
		}
	}

	public static void AddProject(string dbFile, Project project)
	{
		DataFactory df = new DataFactory(dbFile, false);
		using (var sf = df.CreateSessionFactory())
		using (var session = sf.OpenSession())
		using (var trans = session.BeginTransaction())
		{
			session.SaveOrUpdate(project.Author);
			session.SaveOrUpdate(project);
			trans.Commit();
		}
	}
}

Finally, write a little test console app to see if it all works:

class Program
{
	static void Main(string[] args)
	{
		string dbFile = @"C:\temp\test4.db";
		OcDataRepository.Create(dbFile);

		Project project = new Project
		{
			Name = "My First Project",
			Updated = DateTime.Now,
			Author = new Author { Name = "John Smith", Username = "smithj", Email = "jsmith@gmial.com" }
		};
		OcDataRepository.AddProject(dbFile, project);

		Console.WriteLine("done");
		Console.ReadLine();
	}
}

Examine the SQLite db file with your favorite SQLite tool and see the SQL:

CREATE TABLE "Project" (
	Id  integer, 
	Name TEXT, 
	Updated DATETIME, 
	Author_id INTEGER, 
	primary key (Id)
)

If you don’t have 7-zip, download and install it first. Download the code OcWiki.7z (695.8KB)