Experimenting with RabbitMQ – Topic exchange


Preamble

This is part 4 of a series of blogposts about RabbitMQ. This series aims to provide more information (I cannot vouch for the accuracy of the information as I’m a beginner at RabbitMQ) concerning a series of posts by Derek Greer.

Part 1: Experimenting with RabbitMQ – HelloWorldExample

Part 2: Experimenting with RabbitMQ – LoggingApplication example

Part 3: Experimenting with RabbitMQ – Fanout exchange

Part 4: Experimenting with RabbitMQ – Topic exchange

————————

The code provided here demonstrates a topic exchange and running it works fine (you can get the code here). However, then I tried to filter all the “Business” messages to another consumer and here what I tried first:

using System;
using System.IO;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Consumer2
{
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Should show only Business messages");
            Console.WriteLine("==================================");
            const string exchange = "topic-exchange-example";
            const string queue = "log";

            var connectionFactory = new ConnectionFactory();
            var connection = connectionFactory.CreateConnection();
            IModel channel = connection.CreateModel();

            channel.ExchangeDeclare(exchange, ExchangeType.Topic, false, true, null);
            channel.QueueDeclare(queue, false, false, true, null);
            channel.QueueBind(queue, exchange, "*.Business.*");

            var consumer = new QueueingBasicConsumer(channel);
            channel.BasicConsume(queue, true, consumer);

            while (true)
            {
                try
                {
                    var eventArgs = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                    var message = Encoding.UTF8.GetString(eventArgs.Body);
                    Console.WriteLine(string.Format("{0} - {1}", eventArgs.RoutingKey, message));
                }
                catch (EndOfStreamException)
                {
                    // The consumer was cancelled, the model closed, or the connection went away.
                    break;
                }
            }
            channel.Close();
            connection.Close();
        }
    }
}

This is nearly identical to the Consumer application provided in the original post. When I ran this I got:

Topic

This was definitely not what I was expecting because I would have thought that by specifying

channel.QueueBind(queue, exchange, "*.Business.*");

would have been enough to do the filtering.

Upon further thought, what this is, I believe, saying is bind all the messages that match the routing key “*.Business.*” to the queue “log”. Now, if you consider the same code in “Consumer” itself:

channel.QueueBind(queue, exchange, "*.Personal.*");

So effectively we have bound all messages matching “*.Business.*” and “*.Personal.*” to the same queue “log” and we dequeue it, we’ll get a mixture of both. Hence, the solution to that problem is to create two queues “log-personal” and “log-business” which results in:

Topic correctThis in fact corresponds to this diagram:

Posted in .NET, ALT.NET, RabbitMQ

Experimenting with RabbitMQ – Fanout exchange


Preamble

This is part 3 of a series of blogposts about RabbitMQ. This series aims to provide more information (I cannot vouch for the accuracy of the information as I’m a beginner at RabbitMQ) concerning a series of posts by Derek Greer.

Part 1: Experimenting with RabbitMQ – HelloWorldExample

Part 2: Experimenting with RabbitMQ – LoggingApplication example

Part 3: Experimenting with RabbitMQ – Fanout exchange

Part 4: Experimenting with RabbitMQ – Topic exchange

————————

Here are some experiments I carried out based upon this post and here is my version which you can obtain here:

Producer

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using RabbitMQ.Client;

namespace Producer
{
    class Program
    {
        private static volatile bool _cancelling;
        private const string Exchange = "fanout-exchange-example";
        static void Main()
        {
            var connectionFactory = new ConnectionFactory();
            IConnection connection = connectionFactory.CreateConnection();
            IModel channel = connection.CreateModel();
            channel.ExchangeDeclare(Exchange, ExchangeType.Fanout);

            var thread = new Thread(() => PublishQuotes(channel));
            thread.Start();

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();

            _cancelling = true;

            channel.Close();
            connection.Close();
        }

        private static void PublishQuotes(IModel channel)
        {
            while (true)
            {
                if (_cancelling)
                {
                    return;
                }
                IEnumerable<string> quotes = FetchStockQuotes(new[] { "GOOG", "HD", "MCD" });

                foreach (var quote in quotes)
                {
                    byte[] message = Encoding.UTF8.GetBytes(quote);
                    channel.BasicPublish(Exchange, "", null, message);
                }
                Thread.Sleep(5000);
            }
        }

        private static IEnumerable<string> FetchStockQuotes(string[] symbols)
        {
            var quotes = new List<string>();

            string url = string.Format("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20({0})&env=store://datatables.org/alltableswithkeys",
                String.Join("%2C", symbols.Select(s => "%22" + s + "%22")));
            var wc = new WebClient { Proxy = WebRequest.DefaultWebProxy };
            var ms = new MemoryStream(wc.DownloadData(url));
            var reader = new XmlTextReader(ms);
            XDocument doc = XDocument.Load(reader);
            XElement results = doc.Root.Element("results");

            foreach (string symbol in symbols)
            {
                XElement q = results.Elements("quote").First(w => w.Attribute("symbol").Value == symbol);
                quotes.Add(symbol + ":" + q.Element("AskRealtime").Value);
            }

            return quotes;
        }
    }
}

Consumer

using System;
using System.IO;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Consumer
{
    class Program
    {
        private const string Exchange = "fanout-exchange-example";
        private const string Queue = "quotes";

        static void Main()
        {
            var connectionFactory = new ConnectionFactory();
            IConnection connection = connectionFactory.CreateConnection();
            IModel channel = connection.CreateModel();

            channel.ExchangeDeclare(Exchange, ExchangeType.Fanout);
            channel.QueueDeclare(Queue, false, false, true, null);
            channel.QueueBind(Queue, Exchange, "anything");

            var consumer = new QueueingBasicConsumer(channel);
            channel.BasicConsume(Queue, true, consumer);

            Console.WriteLine("In Consumer");
            Console.WriteLine("===========");
            while (true)
            {
                try
                {
                    var eventArgs = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                    var message = Encoding.UTF8.GetString(eventArgs.Body);
                    Console.WriteLine(message);
                    Console.WriteLine("--------");
                }
                catch (EndOfStreamException)
                {
                    // The consumer was cancelled, the model closed, or the connection went away.
                    break;
                }
            }

            channel.Close();
            connection.Close();

        }
    }
}

This code when run worked fine. Then I referred back to this diagram:

and decided to add another consumer

Consumer2

using System;
using System.IO;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Consumer2
{
    class Program
    {
        private const string Exchange = "fanout-exchange-example";
        private const string Queue = "quotes2";

        static void Main()
        {
            var connectionFactory = new ConnectionFactory();
            IConnection connection = connectionFactory.CreateConnection();
            IModel channel = connection.CreateModel();

            channel.ExchangeDeclare(Exchange, ExchangeType.Fanout);
            channel.QueueDeclare(Queue, false, false, true, null);
            channel.QueueBind(Queue, Exchange, "anything");

            var consumer = new QueueingBasicConsumer(channel);
            channel.BasicConsume(Queue, true, consumer);

            Console.WriteLine("In Consumer2");
            Console.WriteLine("===========");
            while (true)
            {
                try
                {
                    var eventArgs = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                    var message = Encoding.UTF8.GetString(eventArgs.Body);
                    Console.WriteLine(message);
                    Console.WriteLine("--------");
                }
                catch (EndOfStreamException)
                {
                    // The consumer was cancelled, the model closed, or the connection went away.
                    break;
                }
            }

            channel.Close();
            connection.Close();

        }
    }
}

Upon running the application both consumers would receive the same information which is what we expected:

Fanout

Redeclaring the exchange to be direct

First I delete the “fanout-exchange-example” exchange using the web interface. Then in “Producer”, “Consumer” and “Consumer2”, I changed the exchange declaration to:

channel.ExchangeDeclare(Exchange, ExchangeType.Direct);

Upon running the application, I got the same results as before. I was initially surprised by this but upon looking at the code, the architecture and the model of RabbitMQ started making more sense. So, let’s break it down by first starting with the Producer and in particular:

channel.ExchangeDeclare(Exchange, ExchangeType.Direct);
channel.BasicPublish(Exchange, "", null, message);

In the Producer, we have only declared the Exchange and its type. We have not bound it to any queue whatsoever. However, notice that we have specified the “routing key” to be the empty string. Bear that in mind as this will make more sense later.

Now in Consumer (the same applies for Consumer2 here), consider the following code:

channel.ExchangeDeclare(Exchange, ExchangeType.Direct);
channel.QueueDeclare(Queue, false, false, true, null);
channel.QueueBind(Queue, Exchange, "");

First we’re declaring an exchange which is the same as in the Producer. Then we also declare a queue where we will push the message. Finally, and very importantly, we bind the Queue to the Exchange and here is the signature of the “QueueBind” method:

QueueBind(string queue, string exchange, string routingKey)

Now according to the RabbitMQ documentation:

A direct exchange delivers messages to queues based on the message routing key. A direct exchange is ideal for the unicast routing of messages 
(although they can be used for multicast routing as well). Here is how it works:
  • A queue binds to the exchange with a routing key K
  • When a new message with routing key R arrives at the direct exchange, the exchange routes it to the queue if K = R

So when we bound the Queue to the Exchange using the empty string routing key (remember that I told you to bear this in mind earlier), the message will get delivered to both Consumer and Consumer2.

To prove that this was indeed the case, what I did was to change the routing key for “Consumer2” by using this:

channel.QueueBind(Queue, Exchange, "anything");

and this was the result:

RoutingKey

Hence, this demonstrates one of the differences between a fanoutexchange and a direct exchange. Had the exchange been a fanout exchange, the routing key wouldn’t have mattered one bit and both Consumer and Consumer2 would have received the same messages.

Declaring both consumers to have the same queue

If you have been following along, then you’ll want to revert the exchange type to being fanout and deleting the existing exchange on the server.

So what I did next was to ensure that both Consumer and Consumer2 were using the “quotes” queue by setting the following in Consumer2

private const string Queue = "quotes";

The result was quite interesting

Same queue

As you can see, each consumer would process the message indeterminately i.e. what message will be processed on which server is not controlled. What can be guaranteed is that the messages will be processed. I guess that this could be used to distribute the processing randomly and possibly even out the load across servers.

Posted in .NET, ALT.NET, RabbitMQ

Experimenting with RabbitMQ – LoggingApplication example


Preamble

This is part 2 of a series of blogposts about RabbitMQ. This series aims to provide more information (I cannot vouch for the accuracy of the information as I’m a beginner at RabbitMQ) concerning a series of posts by Derek Greer.

Part 1: Experimenting with RabbitMQ – HelloWorldExample

Part 2: Experimenting with RabbitMQ – LoggingApplication example

Part 3: Experimenting with RabbitMQ – Fanout exchange

Part 4: Experimenting with RabbitMQ – Topic exchange

————————

These are some notes based upon this blogpost.

In this example, the Producer and Consumer applications differ from the HelloWorldExample in the following ways.

Logging Application’s Producer

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using RabbitMQ.Client;

namespace Producer
{
  class Program
  {
    static void Main(string[] args)
    {
      Thread.Sleep(1000);
      var connectionFactory = new ConnectionFactory();
      IConnection connection = connectionFactory.CreateConnection();
      IModel channel = connection.CreateModel();

      channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Direct);
      string value = DoSomethingInteresting();
      string logMessage = string.Format("{0}: {1}", TraceEventType.Information, value);

      byte[] message = Encoding.UTF8.GetBytes(logMessage);
      channel.BasicPublish("direct-exchange-example", string.Empty, null, message);

      channel.Close();
      connection.Close();
    }

    static string DoSomethingInteresting()
    {
      return Guid.NewGuid().ToString();
    }
  }
}

In this particular instance, the producer explicitly declares a “Direct Exchange” but there is no queue declared. Instead, when we publish the message, we send it to the “direct-exchange-example” exchange and pass the empty string as the routing key.

The author also emphasises the following:

Note that our logging example’s Producer differs from our Hello World’s Producer in that we didn’t declare a queue this time.  
In our Hello World example, we needed to run our Producer before the Consumer since the Consumer simply retrieved a single message and exited.  
Had we published to the default exchange without declaring the queue first, our message would simply have been discarded by the server before 
the Consumer had an opportunity to declare and bind the queue.

So I decided to go back and experiment and declared the empty string as being the queue and the routing key.

HelloWorld’s Producer

static void Main()
{
    const string queue = "";//"hello-world-queue";
    var connectionFactory = new ConnectionFactory();
    IConnection connection = connectionFactory.CreateConnection();

    IModel channel = connection.CreateModel();

    channel.QueueDeclare(queue, false, false, false, null);

    byte[] message = Encoding.UTF8.GetBytes("Hello, World!");
    channel.BasicPublish(string.Empty, queue, null, message);

    Console.WriteLine("Press any key to exit");
    Console.ReadKey();
    channel.Close();
    connection.Close();
}

When I ran HelloWorld’s Producer, three things of note happened:

  1. A message was indeed sent to the server
  2. As Derek Greer pointed out, the server discarded the message i.e. it did not get queued.
  3. Actually a queue was created. It was named amq.gen-wk-vqJ4WsDriToH7XOs0sg  with the part in red being randomly generated. Hence running, this code again created another queue.

EmptyStringForQueue

Logging Application’s Consumer

using System;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Consumer
{
  class Program
  {
    static void Main(string[] args)
    {
      var connectionFactory = new ConnectionFactory();
      IConnection connection = connectionFactory.CreateConnection();
      IModel channel = connection.CreateModel();

      channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Direct);
      channel.QueueDeclare("logs", false, false, true, null);
      channel.QueueBind("logs", "direct-exchange-example", "");

      var consumer = new QueueingBasicConsumer(channel);
      channel.BasicConsume("logs", true, consumer);

      var eventArgs = (BasicDeliverEventArgs) consumer.Queue.Dequeue();

      string message = Encoding.UTF8.GetString(eventArgs.Body);
      Console.WriteLine(message);

      channel.Close();
      connection.Close();
      Console.ReadLine();
    }
  }
}

In the consumer code, there are various aspects which are interesting. First, we are creating a queue which we are calling “logs” but the point to take from this is that the “auto-delete” has been set to true (We’ll return to this later).

The second point to note is that we are binding the queue to the exchange:

channel.QueueBind("logs", "direct-exchange-example", "");

We are retrieving the message differently to HelloWorld’s Consumer:

var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume("logs", true, consumer);
var eventArgs = (BasicDeliverEventArgs) consumer.Queue.Dequeue();

The fact that we are using a BasicConsume instead of the BasicGet will cause the queue to be deleted automatically (remember auto-delete mentioned above) as soon as the application is terminated.

Also, just to complete what Derek Greer mentioned in his post, the Dequeue method is a blocking call. What that means is if we were to run the Consumer application first, execution would be blocked on

var eventArgs = (BasicDeliverEventArgs) consumer.Queue.Dequeue();

until we ran the Producer application which published a message.

One thing that still puzzles me though is this. In the Producer code, when we published the message we didn’t assign a particular routing key.

channel.BasicPublish("direct-exchange-example", string.Empty, null, message);

Then when we retrieved the message, it somehow got put on the “logs” queue.

EDIT: Answer to the last puzzle.

This was in fact addressed in the original post and I quote:

To associate our logs queue with our exchange, we use the QueueBind() method providing the name of the queue, the name of the exchange, and the binding key to filter messages on:
channel.QueueBind("logs", "direct-exchange-example", "");
Posted in .NET, ALT.NET, RabbitMQ

Experimenting with RabbitMQ – HelloWorldExample


Preamble

This is part 1 of a series of blogposts about RabbitMQ. This series aims to provide more information (I cannot vouch for the accuracy of the information as I’m a beginner at RabbitMQ) concerning a series of posts by Derek Greer.

Part 1: Experimenting with RabbitMQ – HelloWorldExample

Part 2: Experimenting with RabbitMQ – LoggingApplication example

Part 3: Experimenting with RabbitMQ – Fanout exchange

Part 4: Experimenting with RabbitMQ – Topic exchange

————————

 

I am currently following the excellent series on RabbitMQ by Derek Greer. The latter give a very good introduction of the basic concepts of RabbitMQ. However, some of the implementation details have been glossed over and I aim to document some of these minor points as I understand them.

From his explanation of the “HelloWorldExample” he has got the following code:

Producer

class Program
{
  static void Main(string[] args)
  {
    var connectionFactory = new ConnectionFactory();
    IConnection connection = connectionFactory.CreateConnection();
    IModel channel = connection.CreateModel();
    channel.QueueDeclare("hello-world-queue", false, false, false, null);
    byte[] message = Encoding.UTF8.GetBytes("Hello, World!");
    channel.BasicPublish(string.Empty, "hello-world-queue", null, message);
    Console.WriteLine("Press any key to exit");
    Console.ReadKey();
    channel.Close();
    connection.Close();
  }
}

Consumer

class Program
{
  static void Main(string[] args)
  {
    var connectionFactory = new ConnectionFactory();
    IConnection connection = connectionFactory.CreateConnection();
    IModel channel = connection.CreateModel();
    channel.QueueDeclare("hello-world-queue", false, false, false, null);
    BasicGetResult result = channel.BasicGet("hello-world-queue", true);
    if (result != null)
    {
      string message = Encoding.UTF8.GetString(result.Body);
      Console.WriteLine(message);
    }
    Console.WriteLine("Press any key to exit");
    Console.ReadKey();
    channel.Close();
    connection.Close();
  }
}

This code works and runs fine but what caught my attention was the fact that both the Producer and Consumer create the queue:

channel.QueueDeclare("hello-world-queue", false, false, false, null);

Hence, the first conclusion that can be drawn is the fact that redeclaring the same queue does not create a new queue on the server.

Read more ›

Posted in .NET, ALT.NET, RabbitMQ

Castle Windsor NLog Integration


Motivation

Castle Windsor comes with what it calls a “Logging Facility“. What the latter allows you to do is very quickly enable logging using a variety of ways but what is most interesting is the fact that the “Logging Facility” provides the necessary plumbing for swapping one type of logger for another. So for instance, you can change from log4net to NLog very easily by simply changing one line of code.

In fact, that’s what I had to do i.e. swap out log4net for NLog because I had started logging using the former but then found out that the latter might possibly be better.

While there are examples of how to use the “Logging Facility” with log4net, I haven’t found any with dealt specifically with NLog integration and Castle Windsor in detail. While the resources provided earlier do help and can be used to figure out how to get NLog integration, the information needed aggregating which this post attempts to address.

Set up Castle Windsor NLog integration

This is very straighforward if you use Nuget. Simply add the package “Castle Windsor NLog” to your project.

Castle Windsor Nlog IntegrationAlso via Nuget install both “NLog Configuration” and “NLog Schema for Intellisense(TM)” to your project.

NLog config and NLog Schema

The former is how you will configure NLog i.e. where to create the log file, etc while the latter will provide intellisense while editing the configuration file.

So here’s how you might want to configure logging:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!--
See http://nlog-project.org/wiki/Configuration_file
for information on customizing logging rules and outputs.
-->
<targets>
<!-- add your targets here -->
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}" />
</targets>
<rules>
<!-- add your logging rules here -->
<logger name="*" minlevel="Trace" writeTo="f" />
</rules>
</nlog>

The next step is to ensure that CastleWindsor will use NLog to perform logging:

container.AddFacility<LoggingFacility>(f => f.LogUsing(LoggerImplementation.NLog)
                                                         .WithConfig("NLog.config"));

If let’s say you want to use log4net instead then instead simply use (assuming you’ve got the “log4.net.config” present):

container.AddFacility<LoggingFacility>(f => f.LogUsing(LoggerImplementation.Log4net)                                                         
                                                         .WithConfig("log4net.config"));

Now according to best practice advice to set up logging:

private ILogger _logger = NullLogger.Instance;
public ILogger Logger
{
get { return _logger; }
set { _logger = value; }
}

Finally, log whatever you want e.g.:

public ActionResult Send()
{
_logger.Info("In Send method");
...
}

I’ve put together the source code here.

Posted in .NET, ALT.NET, Castle.Windsor, IoC, NLog

My ASP.NET MVC 4 solution set up


Introduction

While reading the book “Dependency Injection in .NET“, I had made a mental note that I would want to revisit the architecture of his sample ASP.NET MVC application (pgs 53-54). Most of us are familiar with the 3 layer architecture:

– User Interface Layer

– Domain Logic Layer

– Data Access Layer

What Mark Seemann proposes is creating a new layer, called “Presentation Model Layer” and sliding it in between the “User Interface Layer” and the “Domain Logic Layer” to end up with the “User Interface Layer” being what is known as a “Humble Object“. The essence of this architecture is that it allows us to have the “Views” in MVC in its own project and moving the “Controllers” and “Models” into the “Presentation Model Layer”. Hence, it makes that logical separation that much clearer i.e. only UI code will be in the UI layer and no or minimal logic or C# code should be in that layer.

So while researching how to do this, I came across this post by Jimmy Bogard. He goes one tiny step further than Mark Seeman and actually puts the “Global.asax.cs” file in the “Presentation Model Layer” (“Core” in his blog post).

This post will demonstrate how I did this and I’ll push the boat out a tiny bit further by also talking about using “Razor Generator“. This will however mean that I’ll get C# code in the UI layer but since this is generated and will not be unit tested, I can live with that. It is possible to have this code in a separate layer but I think it just makes more work for no real gain.

Creating the solution and projects

I start off by creating a blank solution “SeparateViewProjectAndRazorGenerator”.

BlankSolution

and then I’ll add an Internet ASP.NET MVC 4 aplication, “UserInterfaceLayer” to that solution.

ASPNET4

Internet Application

At this point should you decide to run the web application, you’ll get the default web application

Site out of the box

So far this should be pretty straightforward and familiar.

Moving code to Presentation Model Layer

For this I’m making use of ReSharper’s move tool (Ctrl R +O) because it will take care of renaming namespaces. Should ReSharper not be available then it will just mean more manual work but the task should by no means be impossible without it.

First I’ll create a class library “PresentationModelLayer” and add it to the solution.

Then I’ll select the “App_Start”, “Controllers”, “Filters” and “Models” folder and using ReSharper’s move tool (Ctrl R +O), move the folders to the “PresentationModelLayer” project.

ResharperMoveFoldersReSharper will warn you that performing this operation might cause problems because the project we’re moving to doesn’t contain references to the code that we are moving requires. Ignore that warning and go ahead.

ReSharperConflicts

After the code has been moved, the solution will no longer compile because of the missing references. Simply use ReSharper to add those missing references (Alt + Enter). One step that you will have to do in order the Views in the Controllers to be “recognised” is to reference the “PresentationModelLayer” in the “UserInterfaceLayer” project.

ReferenceMoreover you will have to reference “System.Net.Http” in the “PresentationModelLayer” project.

System.Net.Http

Moving Global.asax.cs

Unfortunately, this is not as easy as moving the folders but it’s not that difficult either. What you’ll have to do is to go use something like “Windows explorer” and move the “Global.asax.cs” file to the “PresentationModelLayer”.

You will have to ensure that “Show all files” is turned on in Visual Studio and include “Global.asax.cs” in the project. You’ll also want to change the namespace of that file. If the “Global.asax.cs” is still showing up in the “UserInterfaceLayer” in Visual Studio, then simply remove the former.

Global

As soon as this is done, you should now be able to compile and run the solution without any issues.

Add Razor Generator

The instructions are based upon this post and what I have below is simply a condensed version.

  1. Install the Razor Generator Visual Studio add in and you might need to restart Visual Studio at this point.
  2. In “Package Manager Console” enter “Install-Package RazorGenerator.Mvc” ensuring that the “UserInterfaceLayer” is selected.

Install RazorGenerator.mvc

Then say you want to precompile “\SeparateViewProjectAndRazorGenerator\UserInterfaceLayer\Views\Home\Index.cshtml”, set its “Custom Tool” property to “RazorGenerator”

RazorGeneratorAs a result of which you’ll get a “Index.generated.cs” file underneath the “Index.cshtml” file.

GeneratedWhile I was aiming not to have any C# code in the “UserInterfaceLayer” this code is autogenerated and actually represents the view only. So I’m happy to have this C# code in this layer.

This was a bit of a whildwind tour of how I’ve set up my ASP.NET MVC 4 solution but it should get you started onto the road to writing better architected software. Should you be interested in the source code you can find it here.

One drawback to this architecture is the fact that we can’t make use of code scaffolding. This can be mitigated by using an empty ASP.NET MVC project for the “PresentationModelLayer” project but you will still need to move the scaffolded Views to the “UserInterfaceLayer” project.

If you do that however, please be warned that you might get into trouble if you’re deploying to AppHarbor because it will deploy two websites and your site won’t display properly.

Posted in .NET

TDD and Static methods: Resorting to integration testing.


Generally, when we are dealing with unit tests and static methods, we usually have to resort to various tricks in order to mock out the static dependency. The downside to this is that for the sake of unit testing some functionality, you have to add code which in my opinion is not bringing value to the production code and makes the code less readable. Sometimes, writing this extra code is unavoidable unless one resorts to frameworks such as TypeMock, Moles, etc. However, I’ve found an instance whereby mocking this extra code is unnecessary and that case arises when dealing with using LINQ to XML to read some data out (There might be some generic cases for which this applies but I wouldn’t want to pronounce myself until I’ve figured out what these are). The only “downside” is that instead of writing a unit test we would in fact be writing an integration test. I will come back to the latter and explain why I still think that in this particular case, I think it is the way to go.

So consider the following sut:

var document = XElement.Load("file.xml");
return
document.Descendants("patient")
.Where(e => e.Attribute("id").Value == "1")
.Select(e => new PatientRecord
{
FirstName = e.Attribute("firstName").Value
,
LastName = e.Attribute("lastName").Value
,
ExamFindings = e.Descendants("exam")
.OrderByDescending(child => (DateTime)child.Attribute("date"))
.Select(child => new Exam
{
Date = DateTime.Parse(child.Attribute("date").Value)
,
Findings = child.Value
})
})
.Single();
view raw gistfile1.cs hosted with ❤ by GitHub

The first line is “problematic” because we are loading in an external dependency i.e. the file using a static method. Initially, I wanted to apply one of the workarounds as explained in this post. However, I was really reticent to do so and I was really dragging my foot with regards to actually implementing one of these workarounds. After reflection, I realised that what I wanted to do was to pass in a test XML file rather than the production XML file. Hence, using one of the workarounds would would have been overkill because there is much simpler and basic solution which is, instead of hardcoding the XML file path, we can simply pass the file path in as a variable:

var document = XElement.Load(_filePath);

Doing so allows us to use either the test XML file rather than the production XML file. This may seem to be the obvious solution but when people start with unit testing and they encounter static methods, the first thing that pops into their mind might be to dogmatically apply one of the workarounds provided above. However, in order to do that, the code in my workaround would have had to return an XDocument which I would have had to create in my actual test in order to write an actual unit test. This is having to write way too much code in order to test the functionality. So although my solution is actually an integration test, I think it is much better to avoid being dogmatic in this situation and forsake having a unit test for an integration test because effectively, we are testing the functionality in the latter test which is what we want to do at the end of the day. Moreover, since we have control over the test XML file, we can keep it relatively short which ensures that the test doesn’t take that long to run in any case.

For completeness’s sake, in order to set the filepath variable, I make use of constructor injection as follows:

private readonly string _filePath;
public XmlRepository(string filePath)
{
_filePath = filePath;
}
view raw gistfile1.cs hosted with ❤ by GitHub
Tagged with:
Posted in .NET, TDD

Castle Windsor Typed Factory: Resolving dependencies at runtime


Learning about how to use IoC containers, correctly, is quite challenging and while I am still by no means an expert, I am getting a better grasp of how to do it the “proper” way. On my journey, I have encountered the need to resolve a dependency at run time rather than at compile time. So, consider the following code:

public IResult Parse(string[] args)
{
var argumentOption = new ArgumentOption(_dataModelBinder);
var boundArgumentOption = argumentOption.Bind(args);
var bindingResults = boundArgumentOption.Validate(_argumentOptionValidator);
if (bindingResults.Any())
{
return new ErrorResult();
}
return new CreateReportResult(
_resultActioner
, boundArgumentOption.OutputFilePath
, boundArgumentOption.PatientId
, "database");
}

Although this code works as is, there is unfortunately a strong coupling of the IResult which is brought about by instantiating either “ErrorResult” or “CreateReportResult” depending upon the value of “bindingResults”.

The way to deal with this is to use an Abstract Factory i.e. create an abstraction that will resolve a dependency at run time as explained in this blog post. At the end of the latter, there is a mention about how to use Castle Windsor’s typed factory facility, to automagically create these abstract factories. I was having some difficulty actually using the typed factory facility and hence why I am blogging about this.

The main issues

There were two main issues:

  1. Determining the appropriate IResult to create.
  2. Passing the primitive type dependencies required in the instatiation of the CreateReportResult constructor which has the following signature:
public CreateReportResult(IResultActioner resultActioner, Uri filePath, string patientId, string dataSource)

in particular filePath, patientId and dataSource whose values are determined at runtime.

The solution

So here is how I ended up resolving these problems. First, as per the Castle Windsor documentation, we need to create the abstract factory interface definition:

public interface IResultFactory
{
IResult Create(int numOfErrors, Uri filePath, string patientId, string dataSource);
}

The numOfErrors argument is used to address issue 1 above i.e. determining whether to instantiate an ErrorResult or a CreateReportResult. The other arguments will be used in the creation of the latter.

You don’t need to provide an implementation of the interface as Castle Windsor will take care of that under the covers.

This means that the Parse method can now be changed to:

public IResult Parse(string[] args)
{
var argumentOption = new ArgumentOption(_dataModelBinder);
var boundArgumentOption = argumentOption.Bind(args);
var bindingResults = boundArgumentOption.Validate(_argumentOptionValidator);
return _factory.Create(bindingResults.Count()
, boundArgumentOption.OutputFilePath
, boundArgumentOption.PatientId
, "database");
}
view raw ParseIoC.cs hosted with ❤ by GitHub

Then in my IWindsorInstaller implementation I add the following registration code:

//1
container.AddFacility<TypedFactoryFacility>(); 

//2
container.Register(
Component
    .For<IResult>()
    .ImplementedBy<ErrorResult>()
    .Named("ErrorResult")
    );

//3
container.Register(
Component
    .For<IResult>()
    .ImplementedBy<CreateReportResult>()
    .Named("CreateReportResult")
    );

Section 1 is required because we need to tell Castle Windsor that we want to make use of the typed factory facility. Sections 2 and 3 are simply mapping IResult to ErrorResult and CreateReportResult respectively. So far that should hopefully be very familiar and make sense.

We are inching closer to having Castle Windsor instantiating our dependencies but we still haven’t addressed our two issues outline above. In order to do so, we need to make use of ITypedFactoryComponentSelector. The latter contains the logic of which IResult needs to be instantiated is done:

public class CustomTypedFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(System.Reflection.MethodInfo method, object[] arguments)
{
if (method.Name == "Create" && arguments.Length == 4 && arguments[0] is int)
{
if (Convert.ToInt32(arguments[0]) > 0)
{
return "ErrorResult";
}
return "CreateReportResult";
}
return base.GetComponentName(method, arguments);
}
}

Finally, we need to complete the depency registration as follows:

//4
container.Register( 
   Component
   .For<IResultFactory>()
   .AsFactory(c => c.SelectedWith(new CustomTypedFactoryComponentSelector())));

//5
container.Register( 
    Component
    .For<ITypedFactoryComponentSelector>()
    .ImplementedBy<CustomTypedFactoryComponentSelector>());

Section 4 is required otherwise Castle Windsor will use the default ITypedFactoryComponentSelector rather than our  CustomTypedFactoryComponentSelector.

Finally section 5 registers our ITypedFactoryComponentSelector to be  CustomTypedFactoryComponentSelector.

This takes care of issue 1. As far as issue 2 is concerned, it is my understanding that the arguments passed into CustomTypedFactoryComponentSelector are automagically wired up when the CreateReportResult is instantiated as long as the same argument names are used in the AbstractFactory interface (IResultFactory) and the constructor method (CreateReportResult).

When it comes time to instantiating a new IResult, Castle Windsor now has enough information to do so and that’s how you decouple runtime dependencies.

I will end this post by extending my thanks to Martin who replied to my question on StackOverflow.

Posted in Castle.Windsor, IoC

Breakdown of crafting “Crafting Wicked Domain Models”


This post is based upon the video presentation given by Jimmy Bogard on how he crafts Domain Models. The video is available here while the original source is available here. I have also taken the liberty to create my own GitHub repository which will provide various “markers” regarding the various refactorings I made according to the presentation.

I am using this to record what lessons I’ve taken from that presentation and would like to re-iterate that the source code and the refactoring that I will be applying is in no way my work. Now I am doing this because I want to understand some of the more subtle points that he makes by methodically working through his presentation. Moreover, it will be easier and quicker for me to reference certain points in this post as I’m aiming to provide a “summary” of his points.

The problem space

The application that is under consideration concerns a loyalty/reward card programme. This is something that most of us are familiar with whereby you may accumulate points for purchases made. Upon accumulating enough points you can redeem them to get a voucher or a gift. So that’s fairly straightforward at this point.

Presentation walkthrough

He begins the presentation by going through the Anaemic Domain Models of the application.

Member

He starts off with the Member class

using System.Collections.Generic;
namespace Before.Model
{
public class Member : Entity
{
public Member()
{
AssignedOffers = new List<Offer>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public ICollection<Offer> AssignedOffers { get; set; }
public int NumberOfActiveOffers { get; set; }
}
}

He then highlights the fact that “NumberOfActiveOffers” is being used to quickly retrieve and display to the user the persisted number of offers associated with a particular Member, rather than having to calculate it every time the details of a Member is retrieved.

Moreover, he points out that the AssignedOffers only belongs to the given Member and cannot belong to another Member.

Offer

Then he moves onto the Offer class

namespace Before.Model
{
public class Offer : Entity
{
public Member MemberAssigned { get; set; }
public OfferType Type { get; set; }
public DateTime DateExpiring { get; set; }
public int Value { get; set; }
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

He points out that the MemberAssigned denotes the fact that for any given Offer we can know with which Member this Offer is assigned to.

OfferType

namespace Before.Model
{
public class OfferType
{
public string Name { get; set; }
public ExpirationType ExpirationType { get; set; }
public int DaysValid { get; set; }
public DateTime? BeginDate { get; set; }
}
}

This is just metadata about an Offer.

ExpirationType

Finally the ExpirationType is simply an enumeration.

namespace Before.Model
{
public enum ExpirationType
{
Assignment = 1,
Fixed = 2
}
}

So far so good.

However, since these Domain Models are anaemic, i.e. they don’t contain any behaviour, it means that the business logic is not modelled adequately. The latter is actually in OfferAssignmentService

using System;
using WickedDomainModels.Model;
namespace WickedDomainModels.Services
{
public class OfferAssignmentService
{
private readonly IMemberRepository _memberRepository;
private readonly IOfferTypeRepository _offerTypeRepository;
private readonly IOfferValueCalculator _offerValueCalculator;
private readonly IOfferRepository _offerRepository;
public OfferAssignmentService(
IMemberRepository memberRepository,
IOfferTypeRepository offerTypeRepository,
IOfferValueCalculator offerValueCalculator,
IOfferRepository offerRepository
)
{
_memberRepository = memberRepository;
_offerTypeRepository = offerTypeRepository;
_offerValueCalculator = offerValueCalculator;
_offerRepository = offerRepository;
}
public void AssignOffer(Guid memberId, Guid offerTypeId)
{
var member = _memberRepository.GetById(memberId);
var offerType = _offerTypeRepository.GetById(offerTypeId);
var value = _offerValueCalculator
.CalculateValue(member, offerType);
DateTime dateExpiring;
switch (offerType.ExpirationType)
{
case ExpirationType.Assignment:
dateExpiring = DateTime.Now.AddDays(offerType.DaysValid);
break;
case ExpirationType.Fixed:
if (offerType.BeginDate != null)
dateExpiring =
offerType.BeginDate.Value.AddDays(offerType.DaysValid);
else
throw new InvalidOperationException();
break;
default:
throw new ArgumentOutOfRangeException();
}
var offer = new Offer
{
MemberAssigned = member,
Type = offerType,
Value = value,
DateExpiring = dateExpiring
};
member.AssignedOffers.Add(offer);
member.NumberOfActiveOffers++;
_offerRepository.Save(offer);
}
}
}

What should be immediately apparent is that the AssignOffer is much too big and doing too many things. The next section details how to mainly refactor that method.

Upon closer inspection the method is doing the following:

Calculate the value of the Offer

var member = _memberRepository.GetById(memberId);
 var offerType = _offerTypeRepository.GetById(offerTypeId);
var value = _offerValueCalculator
 .CalculateValue(member, offerType);

Calculate the expiry date of the Offer

switch (offerType.ExpirationType)
{
    case ExpirationType.Assignment:
        dateExpiring = DateTime.Now.AddDays(offerType.DaysValid);
        break;
    case ExpirationType.Fixed:
        if (offerType.BeginDate != null)
        dateExpiring =
        offerType.BeginDate.Value.AddDays(offerType.DaysValid);
    else
        throw new InvalidOperationException();
        break;
    default:
        throw new ArgumentOutOfRangeException();
}

Assign the offer to the member

var offer = new Offer
{
    MemberAssigned = member,
    Type = offerType,
    Value = value,
    DateExpiring = dateExpiring
};

member.AssignedOffers.Add(offer);

Increment the Number of Active Offer(s)

member.NumberOfActiveOffers++;

Persist the Offer

_offerRepository.Save(offer);

What is not immediately apparent is the importance of

member.AssignedOffers.Add(offer);
member.NumberOfActiveOffers++;

which the developer needs to remember to have in the code so that tracking of offers is done appropriately. This can be easily missed and that’s yet another reason why having “proper” Domain Models is important.

Refactoring

Hence what we need to do is to refactor the code i.e. we need Domain Models which:

  1. Are responsible for their operational behaviours.
  2. Provide proper encapsulation.

Problem 1

What would happen if we were to add code like:

member.AssignedOffers.Add(offer);
member.AssignedOffers.Clear();
member.NumberOfActiveOffers++;

From a Product Owner POV, it’s a cardinal sin because it would be taking offers from a Member! However, the API, as it is, allows for that operation all too easily. In order to fix that, he applies the Encapsulate Collection refactoring.

Solution 1

So he creates a method representative of what we are trying to achieve i.e. assign an Offer to a Member.

member.AssignOffer(offer);

and getting rid of the code which was assigning the Offer straight to the collection. He then performs a lot of refactoring to the Member class which now looks like:

using System.Collections.Generic;

namespace WickedDomainModels.Model
{
    public class Member : Entity
    {
        private readonly List<Offer> _assignedOffers;

        public Member()
        {
            _assignedOffers = new List<Offer>();
        }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }

        public IEnumerable<Offer> AssignedOffers
        {
            get { return _assignedOffers; }
            // Removed set
        }

        public int NumberOfActiveOffers { get; set; }

        public void AssignOffer(Offer offer)
        {
            _assignedOffers.Add(offer);
        }
    }
}
  • First,  the ICollection has been changed to an IEnumerable. This is due to the fact that using the former you can do various invalid operations e.g.
member.AssignedOffers.CopyTo(...);
  • Secondly, there is no direct access to the collection as the setter for AssignedOffers property has been removed. This prevents external classes from adding to the collection and only allowing  the Member class to add an Offer. Of course, to be in a position to do that, we must have a private field _assignedOffers.

Problem 2

var member = new Member();
member.AssignOffer(new Offer());

Again this from a Product Owner’s point of view, should not be possible. This is effectively saying “Give some ’empty’ offer to someone whose credentials we don’t have on record” (Assuming that one of the aims of the reward card programme is to identify who the people not spending much are).

So what we need to do is to introduce the invariants of the Member and Object classes.

Solution 2

The simple way to do that would be to use constructor injection.

public Member(List<Offer> assignedOffers, string firstName, string lastName, string email)
{
_assignedOffers = assignedOffers;
FirstName = firstName;
LastName = lastName;
Email = email;
}
public Offer(Member memberAssigned, OfferType type, DateTime dateExpiring, int value)
{
MemberAssigned = memberAssigned;
Type = type;
DateExpiring = dateExpiring;
Value = value;
}

So for the Member class we don’t have the empty constructor anymore while for the Object class, we introduce a constructor. (In a later part of the video presentation, he also does the same thing with OfferType (00:42:39))

Problem 3

We previously mentioned that whenever we assign an offer to a Member, we need to remember to increment the NumOfActiveOffers for that Member.

member.AssignOffer(offer);            
member.NumberOfActiveOffers++;

Solution 3

The correct implementation should hence not be in OfferAssignmentService.AssignOffer but rather internally in Member.AssignOffer.

public void AssignOffer(Offer offer)
{
    _assignedOffers.Add(offer);
    NumberOfActiveOffers++;
}

Problem 4

How about this code:

member.AssignOffer(offer);
member.NumberOfActiveOffers = Int32.MaxValue;

While this will compile, this kind of operation should not be possible from a business logic point of view. This is of course caused by the fact that we have got a public setter on NumberOfActiveOffers.

Solution 4

So the solution is fairly simple and is as follows:

public int NumberOfActiveOffers { get; private set; }

This is just good encapsulation. So the responsibility to keep track of the NumberOfActiveOffers is now rightly given to the Member class.

Problem 5

Although we’ve created a constructor for the Object class in order to provide the invariants, there is still an issue i.e.:

var value = Int32.MaxValue;
var offer = new Offer(member, offerType, dateExpiring, value);

This compiles but from a Domain Model POV, this should not be valid.

If you remember from the code above, the actual value to be passed in is calculated by:

var value = _offerValueCalculator.CalculateValue(member, offerType);

and we have to somehow tether this value to the Offer instance creation.

Moreover, we want to prevent code such as:

var offer = new Offer(new Member(...), offerType, dateExpiring, value);

because with this we could be assigning the Offer to the wrong Member.

Solution 5

The way to achieve this is to make the Member class responsible for creating the Offer as well as having it calculating the value.

public void AssignOffer(Guid memberId, Guid offerTypeId)
{
    var member = _memberRepository.GetById(memberId);
    var offerType = _offerTypeRepository.GetById(offerTypeId);

    DateTime dateExpiring;

    // No longer calculating the offer value in this class

    switch (offerType.ExpirationType)
    {
        case ExpirationType.Assignment:
            dateExpiring = DateTime.Now.AddDays(offerType.DaysValid);
            break;
        case ExpirationType.Fixed:
            if (offerType.BeginDate != null)
                dateExpiring =
                    offerType.BeginDate.Value.AddDays(offerType.DaysValid);
            else
                throw new InvalidOperationException();
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
    // No longer instantiating the Offer here.   
    var offer = member.AssignOffer(offerType, dateExpiring, _offerValueCalculator);

    _offerRepository.Save(offer);
}

//////////////////////////////////////////////////////////
public Offer AssignOffer(OfferType offerType, DateTime dateExpiring
            , IOfferValueCalculator offerValueCalculator)
{
    var value = offerValueCalculator
        .CalculateValue(this, offerType);

    var offer = new Offer(this, offerType, dateExpiring, value);

    _assignedOffers.Add(offer);
    NumberOfActiveOffers++;

    return offer;
}

Problem 6

In the code, we would really like to get rid of the switch statement because it violates the OCP.

switch (offerType.ExpirationType)
{
    case ExpirationType.Assignment:
        dateExpiring = DateTime.Now.AddDays(offerType.DaysValid);
        break;
    case ExpirationType.Fixed:
        if (offerType.BeginDate != null)
            dateExpiring =
                offerType.BeginDate.Value.AddDays(offerType.DaysValid);
        else
            throw new InvalidOperationException();
        break;
    default:
        throw new ArgumentOutOfRangeException();
}

Solution 6 ()

The way he resolves this issue is by first pushing date expiry calculation into the AssignOrder method.

public Offer AssignOffer(OfferType offerType /*, Removed dateExpiring */ 
            , IOfferValueCalculator offerValueCalculator)
{
    var value = offerValueCalculator
        .CalculateValue(this, offerType);

    DateTime dateExpiring;

    switch (offerType.ExpirationType)
    {
        case ExpirationType.Assignment:
            dateExpiring = DateTime.Now.AddDays(offerType.DaysValid);
            break;
        case ExpirationType.Fixed:
            if (offerType.BeginDate != null)
                dateExpiring =
                    offerType.BeginDate.Value.AddDays(offerType.DaysValid);
            else
                throw new InvalidOperationException();
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }

    var offer = new Offer(this, offerType, dateExpiring, value);

    _assignedOffers.Add(offer);
    NumberOfActiveOffers++;

    return offer;
}

The switch statement issue is still preseent and he then points out that normally he would try and push that behaviour into the ExpirationType ‘class’. However, if you recall, the latter is actually an enumeration. So, he then suggests mimicking Java based enumerations which can behave more like classes and he does so through the use the following class :

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace WickedDomainModels.Model
{
[Serializable]
public abstract class Enumeration : IComparable
{
private readonly int _value;
private readonly string _displayName;
protected Enumeration()
{
}
protected Enumeration(int value, string displayName)
{
_value = value;
_displayName = displayName;
}
public int Value
{
get { return _value; }
}
public string DisplayName
{
get { return _displayName; }
}
public override string ToString()
{
return DisplayName;
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
return fields.Select(info => info.GetValue(null)).OfType<T>();
}
public static IEnumerable GetAll(Type type)
{
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
return fields.Select(info => info.GetValue(null));
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromValue<T>(int value) where T : Enumeration
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static T FromDisplayName<T>(string displayName) where T : Enumeration
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new ApplicationException(message);
}
return matchingItem;
}
public virtual int CompareTo(object other)
{
return Value.CompareTo(((Enumeration)other).Value);
}
}
}
view raw Enumeration hosted with ❤ by GitHub

The only thing you really need to know about this class is that it is similar to a .NET enum but on steroids.

What he does next is to change the enum ExpirationType to a class deriving from the Enumeration class.

Then, he exposes two properties ‘Assignment’ and ‘Fixed’ which are of type ExpirationType and to get those types he creates a private constructor as he doesn’t want external clients to be able to create an ExpirationType. In this manner, it make the latter seem like an enumeration because you will refer to each “enumeration” as:

ExpirationType.Assignment
ExpirationType.Fixed

Here is the ExpirationType class:

namespace WickedDomainModels.Model
{
public class ExpirationType : Enumeration
{
private ExpirationType(int value, string displayName)
: base(value, displayName)
{
}
public static readonly ExpirationType Assignment = new ExpirationType(1, "Assignment");
public static readonly ExpirationType Fixed = new ExpirationType(1, "Fixed");
}
}
view raw ExpirationType hosted with ❤ by GitHub

The fact that we have changed from an enumeration to a class means that we can no longer use the switch statement but have to resort to ifs (00:51:22).

public Offer AssignOffer(OfferType offerType
            , IOfferValueCalculator offerValueCalculator)
{
    var value = offerValueCalculator
        .CalculateValue(this, offerType);

    var dateExpiring = new DateTime();

    if (offerType.ExpirationType == ExpirationType.Assignment)
        dateExpiring = DateTime.Now.AddDays(offerType.DaysValid);
    else if (offerType.ExpirationType == ExpirationType.Fixed)
    {
        if (offerType.BeginDate != null)
        {
            dateExpiring = offerType.BeginDate.Value.AddDays(offerType.DaysValid);
        }
        // Notice that we haven't got the else throwing an InvalidOperationException
        // here because we are going to deal with this later.
    }
    else
    {
        throw new InvalidOperationException();
    }

    var offer = new Offer(this, offerType, dateExpiring, value);

    _assignedOffers.Add(offer);
    NumberOfActiveOffers++;

    return offer;
}

At this point, you might be wondering why we did all of this work to produce what is arguably less readable code. Well this work has now allowed us to push the behaviour down into  OfferType. The reason why we are using the latter is due to the fact that in the ifs, we can see that there is a offerType is being called many times (code in blue above) and that’s an indication of the Inappropriate Intimacy code smell.

using System;

namespace WickedDomainModels.Model
{
    public class OfferType
    {
        public OfferType(string name, ExpirationType expirationType, int daysValid, DateTime? beginDate)
        {
            Name = name;
            ExpirationType = expirationType;
            DaysValid = daysValid;
            BeginDate = beginDate;
        }

        public string Name { get; set; }
        public ExpirationType ExpirationType { get; set; }
        public int DaysValid { get; set; }
        public DateTime? BeginDate { get; set; }

        public DateTime CalculateExpiration()
        {
            var dateExpiring = new DateTime();

            if (ExpirationType == ExpirationType.Assignment)
                dateExpiring = DateTime.Now.AddDays(DaysValid);
            else if (ExpirationType == ExpirationType.Fixed)
            {
                if (BeginDate != null)
                {
                    dateExpiring = BeginDate.Value.AddDays(DaysValid);
                }
                // Notice that we haven't got the else throwing an InvalidOperationException
                // here because we are going to deal with this later.
            }
            else
            {
                throw new InvalidOperationException();
            }
            return dateExpiring;
        }
    }
}

This is looking more promising but there are still the conditional strutures. Well how about we push down the behaviour down to the ExpirationType class i.e. doing what in the DDD world is know about double dispatch (This last bit is quoting the author and I don’t know what this is exactly)?

using System;

namespace WickedDomainModels.Model
{
    public class ExpirationType : Enumeration
    {
        private ExpirationType(int value, string displayName)
            : base(value, displayName)
        {
        }
        public static readonly ExpirationType Assignment = new ExpirationType(1, "Assignment");
        public static readonly ExpirationType Fixed = new ExpirationType(1, "Fixed");

        public DateTime CalculateExpiration(OfferType offerType)
        {
            var dateExpiring = new DateTime();

            if (this == Assignment)
                dateExpiring = DateTime.Now.AddDays(offerType.DaysValid);
            else if (this == Fixed)
            {
                if (offerType.BeginDate != null)
                {
                    dateExpiring = offerType.BeginDate.Value.AddDays(offerType.DaysValid);
                }
                // Notice that we haven't got the else throwing an InvalidOperationException
                // here because we are going to deal with this later.
            }
            else
            {
                throw new InvalidOperationException();
            }
            return dateExpiring;
        }
    }
}

Looking better but still got the ugly ifs. We will now take care of this and the way to do that is to make use of polymorphism as follows:

using System;

namespace WickedDomainModels.Model
{
    public abstract class ExpirationType : Enumeration
    {
        private ExpirationType(int value, string displayName)
            : base(value, displayName)
        {
        }

        public static readonly ExpirationType Assignment = 
            new AssignmentType();
        public static readonly ExpirationType Fixed = 
            new FixedType();

        public abstract DateTime CalculateExpiration(OfferType offerType);

        private class AssignmentType: ExpirationType{
            public AssignmentType() 
                : base(1, "Assignment")
            {
            }

            public override DateTime CalculateExpiration(OfferType offerType)
            {
                return DateTime.Now.AddDays(offerType.DaysValid);
            }
        }

        private class FixedType : ExpirationType
        {
            public FixedType()
                : base(2, "Fixed")
            {
            }

            public override DateTime CalculateExpiration(OfferType offerType)
            {
                return offerType.BeginDate.Value.AddDays(offerType.DaysValid);
            }
        }
    }
}

Before we go into the details of the code above let me remind you of the OfferType constructor:

public OfferType(string name, ExpirationType expirationType, int daysValid, DateTime? beginDate)

So the expirationType can either be “ExpirationType.Assigned” or “ExpirationType.Fixed”.

Now back to the code and in particular pay attention to the code in green which shows how by assigning a property (Assigned or Fixed) to the  ExpirationType will in turn create the correct subclass of the latter. So the property is determining which subclass is being created.

Then the code in red is used to declare an abstract method CalculateExpiration so that we can call

ExpirationType.CalculateExpiration(offerType)

we can delegate to the appropriate implemenation of the method in AssignmentType and FixedType shown in blue. Note how the latter two are declared as private classes because we don’t wan’t external clients to be able to instantiate an instance of either subclass. We have actually applied the strategy pattern here i.e. AssignmentType and FixedType are the strategies. Oh and one final thing which you might have not noticed is the fact that we no longer have an else statement which is throwing an exception since we are using strategies to calculate the expiry date.

I must however admit that I’m not too enamoured with this strategy pattern implementation because the class can get huge should the number of strategies increase significantly.

Conclusion

One of the conclusions that Jimmy Bogard had was that, as this walkthrough has shown, you can start with a poorly modelled domain but by refactoring, you can get a much better Domain Model which has the appropriate API and also is more decoupled.

Personally, I have learned a few things:

  • Use the appropriate language features. For instance, the code originally used an ICollection which was then changed to an IEnumerable. This meant that we couldn’t do funny things such as CopyTo. So this was a concrete demontration of the principle of least priviledge.
  • Try not to use enumerations but instead do something similar to what the ExpirationType did. This will help get rid of conditional structures.
  • Having an anaemic class might be an indication that the code actually needs refactoring.
Tagged with: , , , ,
Posted in .NET, 2012, NDC Walkthrough

Enterprise library Validation block versus FluentValidation


On the current code I’m working on, I wanted to use Enterprise library validation block (EntLibVal) but I ended up with Fluent validation (FV) to validate my input. I settled on the latter for the the former was causing me headaches in the following manner:

  • Configuration using the “Enterprise configuration console” was buggy in my opinion. I had created a custom validator and when it came to its configuration, I didn’t get the option to load my custom validator however much I tried loading the appropriate dll. I wanted this to work so that I didn’t have to pollute my domain class with data annotation.
  • As a result, I had to begrudgingly resort to using data annotations but that really polluted my class which I didn’t really like.
  • The deal breaker for me was how difficult it was to try and use Castle Windsor as my IoC container (EntLibVal comes with Unity as the default IoC). That proved to be the straw that broke the camel’s back.
    • Although there is supposed to be a Castle Windsor extension, unfortunately the latter is using an older version of Castle Windsor (2.5.1) (I was using 3.2.0). That means you will run into the following problem. Once you get past that hurdle using the solution provided in the SO post, you then run into a problem with the implementation of the extension. The solution might be to look at this project and maybe to implement your own instance of IContainerConfigurator. In my opinion, this is way too much ceremony just to use another IoC container.
  • To get started with EntLibVal is well documented but to do more involved things e.g. using a different IoC container, was just plain undocumented.

So I then settled on FV and I did what I had to do in 30 minutes whereas I spent the best part of two days not really getting to where I wanted to be at using EntLibVal. The latter is not a bad product but there was too much faffing around to my liking and I really have better things to do than to try and understand the internals of the EntLibVal.

Posted in .NET, Enterprise Library Validation Block, FluentValidation

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 3 other followers

May 2022
M T W T F S S
 1
2345678
9101112131415
16171819202122
23242526272829
3031