computers are cool

mookid on code

Assuring that those IWantToRunAtStartup can actually run at startup

November 25th, 2010 by mookid

When building NServiceBus services based on the generic host, you may need to do some stuff whenever your service starts up and shuts down. The way to do that is to create classes that implement IWantToRunAtStartup, which will be picked up by the host and registered in the container as an implementation of that interface.

When the time comes to run whoever wants to run at startup, the host does a

container.ResolveAll<IWantToRunAtStartup>()

to get all the relevant instances (or something similar if you aren’t using Windsor…).

If, however, one or more instances cannot be instantiated due to missing dependencies, you will get no kind of warning or error whatsoever! 1 This means that the service will silently ignore the fact that one or more IWantToRunAtStartups could not be instantiated and run.

In order to avoid this error, I have written a test that looks somewhat like this:

[Test]
public void WhoeverWantsToRunAtStartupCanActuallyRun()
{
  var container = new WindsorContainer();
 
  PerformTheUsualRegistration(container);
 
  var typesThatWantToRun = from type in typeof (MyService.EndpointConfiguration).Assembly.GetTypes()
                         where typeof(IWantToRunAtStartup).IsAssignableFrom(type) 
                             && !type.IsAbstract && !type.IsInterface 
                         select type;
 
  ManuallyRegister(container, typesThatWantToRun);
  RegisterFakeBus(container);
 
  var typesThatCouldRun = container.ResolveAll<IWantToRunAtStartup>().Select(c => c.GetType());
  var typesThatCouldNotRun = typesThatWantToRun.Except(typesThatCouldRun);
 
  if (typesThatCouldNotRun.Any())
  {
    Assert.Fail(string.Join(Environment.NewLine + Environment.NewLine,
          typesThatCouldNotRun.Select(t => GenerateErrorDetailsFor(t, container)).ToArray()));
  }
}
 
string GenerateErrorDetailsFor(Type type, IWindsorContainer container)
{
  // first, register the class as itself if is has not already been done
  if (!container.Kernel.HasComponent(type))
  {
    container.Register(Component.For(type).Named(type.name + " that wants to run"));
  }
 
  // next, make Windsor throw an exception with all the nasty details...
  var exceptionText = "";
 
  try
  {
    container.Resolve(type);
  }
  catch (HandlerException e)
  {
    exceptionText = e.Message;
  }
 
  return string.Format(@"Class: {0}
 
Reason:
 
{1}", type.Name, exceptionText);
}
 
void PerformTheUsualRegistration(IWindsorContainer container)
{
  container.Install(FromAssembly.Containing<MyService.EndpointConfiguration>());
}
 
void ManuallyRegister(IWindsorContainer container, IEnumerable<Type> typesThatWantToRun)
{
  container.Register(typesThatWantToRun
                        .Select(t => Component.For<IWantToRunAtStartup>().ImplementedBy(t))
                        .ToArray());
}
 
void RegisterFakeBus(IWindsorContainer container) 
{
  container.Register(Component.For<IBus>().Instance(MockRepository.GenerateMock<IBus>());
}

I admit that the code is kind of clunky even though I distilled the interesting parts from some of the plumbing in our real test… moreover, our real test is iterating through all the possible configurations our container can have – one for each environment – so you can probably imagine that it’s not pretty :)

But who cares??? The test has proven almost infinitely useful already! Whenever something that wants to run at startup cannot run at startup, TeamCity gives us error messages like this:

Class: RunSomething
 
Reason:
 
Can't create component 'RunSomething that wants to run'  as it has dependencies to be satisfied. 
RunSomething that wants to run is waiting for the following dependencies: 
 
Services: 
- OneOfOurProjects.Api.ISomethingElse which was not registered.
  1. At least this is the case when using Castle Windsor – I don’t know if this is also the behavior of other IoC containers… Maybe someone can clarify this…?

Fun with RavenDB 3

November 8th, 2010 by mookid

This is the third post in a small series about RavenDB – a document database in .NET. I will try to touch the same areas as I did in my series on MongoDB, possibly comparing the two where I see fit.

Now, this time we will take a look at some more querying…

Introduction

After having chewed a bit on the concept of map/reduce queries from doing my MongoDB series, I am actually beginning to see the beauty of this kind of query – and of course RavenDB supports them as well, because it is currently the only sane way to structure aggregate queries in distributed databases.

All of RavenDB’s queries are actually map/reduce queries, but if you don’t supply a reduction function, the reduction is trivial, as every document is emitted in its entirety.

Now, let’s try specifying our own reduction function….

Simple map/reduce query

But first we’ll start out by stuffing some data in the DB:

using (var session = documentStore.OpenSession())
{
    session.Store(new Order
                        {
                            Items =
                                {
                                    new Item {Name = "beer", Amount = 12},
                                    new Item {Name = "peanuts", Amount = 3},
                                    new Item {Name = "cashew nuts", Amount = 4},
                                }
                        });
 
    session.Store(new Order
                        {
                            Items =
                                {
                                    new Item {Name = "beer", Amount = 6},
                                    new Item {Name = "just nuts", Amount = 8},
                                    new Item {Name = "peanuts", Amount = 6},
                                    new Item {Name = "cashew nuts", Amount = 2},
                                }
                        });
 
    session.Store(new Order
                        {
                            Items =
                                {
                                    new Item {Name = "beer", Amount = 5},
                                }
                        });
 
    session.SaveChanges();
}

Now, in order to aggregate each Item by name, summing up the amounts, let’s construct the following index:

 
public class AggregateAmountsPerItem : AbstractIndexCreationTask
{
    public override IndexDefinition CreateIndexDefinition()
    {
        return new IndexDefinition<Order, ItemAggregate>
                    {
                        Map = orders => from order in orders
                                        from item in order.Items
                                        select new {item.Name, item.Amount},
                        Reduce = items => from item in items
                                            group item by item.Name into i
                                            select new {Name = i.Key, Amount = i.Sum(x => x.Amount)}
                    }.ToIndexDefinition(DocumentStore.Conventions);
    }
}

Note how this way of structuring the map/reduce differs from MongoDB and CouchDB where the map operation decides which value to aggregate on by emitting it as the key – in RavenDB, this decision is made in the reduce function.

Note also that I need to create a type, ItemAggregate, that is used to tell which fields the reduce function should expect as input. It is important that this type’s fields correspond to those emitted from the map function, or else the serialization will fail silently, yielding no results.

Now, let’s execute the index creation like so:

IndexCreation.CreateIndexes(typeof (AggregateAmountsPerItem).Assembly, documentStore);

and now I am ready to query the index:

using(var session = documentStore.OpenSession())
{
    var amountsPerItem = session.Query<ItemAggregate>(typeof(AggregateAmountsPerItem).Name);
 
    foreach(var amountPerItem in amountsPerItem)
    {
        Console.WriteLine("{0}: {1} pcs", amountPerItem.Name, amountPerItem.Amount);
    }
}

which yields the following results:

Executing query '' on index 'AggregateAmountsPerItem' in 'http://localhost:8080'
Query returned 4/4 results
beer: 23 pcs
peanuts: 9 pcs
cashew nuts: 6 pcs
just nuts: 8 pcs

and that was pretty much what I expected.

That was a quick look at map/reduce queries in RavenDB. I’m sure there’s no end to the fun you can have with this kind of stuff, but I have yet to use map/reduce for anything in a real project, so I can’t really comment further on those.

Fun with RavenDB 2

November 5th, 2010 by mookid

This is the second post in a small series about RavenDB – a document database in .NET. I will try to touch the same areas as I did in my series on MongoDB, possibly comparing the two where I see fit.

Now, this time we will take a look at querying…

Introduction

Querying with MongoDB is extremely easy and intuitive when you’re used to relational databases – just type in some queries in the form of JSON documents and send them to the server and let it do its work.

Querying with RavenDB is a different story, because the only thing that can be queried is an index1. Let’s see…

Simple query

Let’s start out by handing a couple of documents to the server. Let’s execute the following:

using (var session = documentStore.OpenSession())
{
    session.Store(new Movie {Title = "The Big Lebowski", ViewCount = 200});
    session.Store(new Movie {Title = "Fear And Loathing In Las Vegas", ViewCount = 100});
    session.Store(new Movie {Title = "Adaptation", ViewCount = 20});
 
    session.SaveChanges();
}

Now, in order to be able to query these documents, we need to tell RavenDB to create an index… I like to be code-driven when I can, so let’s do it with C#… first, make a class somewhere derived from AbstractIndexCreationTask:

public class MoviesByViewCount : AbstractIndexCreationTask
{
    public override IndexDefinition CreateIndexDefinition()
    {
        return new IndexDefinition<Movie>
                    {
                        Map = movies => from movie in movies
                                        select new {movie.ViewCount}
                    }.ToIndexDefinition(DocumentStore.Conventions);
    }
}

In this example, I build the index purely from mapping the collection – i.e. there’s no reduce step. Note that the map step is a LINQ query that maps each document into the fields that should be used to build the index. That means that the example above will allow me to query this index and constrain by ViewCount of each movie.

Next, upon initialization, we need to tell RavenDB to create our indexes (if they have not already been created – otherwise, they’ll be updated):

var documentStore = new DocumentStore{...};
documentStore.Initialize();
 
IndexCreation.CreateIndexes(typeof (MoviesByViewCount).Assembly, documentStore);

That was easy. Now, let’s take the index for a spin… first let’s just get everything 2:

using(var session = documentStore.OpenSession())
{
    var movies = from m in session.Query<Movie>(typeof (MoviesByViewCount).Name)
                 select m;
 
    foreach(var movie in movies)
    {
        Console.WriteLine("Got {0} ({1} views)", movie.Title, movie.ViewCount);
    }
}

which results in the following output in the console:

Executing query '' on index 'MoviesByViewCount' in 'http://localhost:8080'
Query returned 3/3 results
Got The Big Lebowski (200 views)
Got Fear And Loathing In Las Vegas (100 views)
Got Adaptation (20 views)

which is pretty much what we expected. Note however that RavenDB is nice enough to tell when a query is executed and how many results are returned.

Now let’s use our index and change the query into this:

var movies = from m in session.Query<Movie>(typeof (MoviesByViewCount).Name)
                where m.ViewCount <= 100
                select m;

which gives me the following output:

Executing query 'ViewCount_Range:[* TO 0x00000064]' on index 'MoviesByViewCount' in 'http://localhost:8080'
Query returned 2/2 results
Got Fear And Loathing In Las Vegas (100 views)
Got Adaptation (20 views)

Note how the query criteria are translated into a Lucene query – that’s because RavenDB uses Lucene. NET for all of its indexing work. Just for the fun of it, let’s try using the index to constrain by title:

var movies = from m in session.Query<Movie>(typeof (MoviesByViewCount).Name)
             where m.Title == "The Big Lebowski"
             select m;

which results in the following output:

Executing query 'Title:[["The Big Lebowski"]]' on index 'MoviesByViewCount' in 'http://localhost:8080'
Query returned 0/0 results

It appears RavenDB will just go ahead and query Lucene for it, even though the index doesn’t have the specified field. Kind of weird, but maybe it’s because Lucene is itself a document DB, and there’s no way to tell beforehand whether a given index contains a document with the specified field.

Now that was a couple of simple queries. Next time, let’s try building a map/reduce query!

  1. Or is it? I should point out that Ayende said that RavenDB would have dynamically generated temporary indexes in the future, allowing ad-hoc quering… but what’s more, those temporary indexes would “materialize” and become permanent if you hit them enough times… that actually sounds extremely cool, and should allow for some truly frictionless and agile-feeling development.
  2. Note that because of RavenDB’s excellent safe-by-default philosophy, at most 128 documents will be returned! Therefore, the usual .Skip(n) and .Take(m) methods should be used to properly page the result sets.

    Note also, that by default you can perform only 30 operations resulting in remote calls within one IDocumentSession. This is another constraint that will guide you away from blowing off that left foot of yours :)

Fun with RavenDB 1

November 1st, 2010 by mookid

This is the first post in a small series about RavenDB – a document database in .NET. I will try to touch the same areas as I did in my series on MongoDB, possibly comparing the two where I see fit.

Now, first – let’s see if the raven can fly…

Getting started

I am extremely happy to see that Ayende has created the same installation experience as I got with MongoDB… i.e., to get the server running, perform the following steps (assuming the .NET 4 framework is installed on your system):

  1. Grab a ZIP with the lastest build here
  2. Unzip somewhere
  3. Go to /Server and run Raven.Server.exe

- and now the RavenDB server will be running on localhost:8080. That was easy. Now, try visiting http://localhost:8080 in your browser – now you should see the administration interface of RavenDB.

By the way, have you ever tried installing Microsoft SQL Server? Shudder!! :)

Connecting with the .NET client

I’m old school, so I am still using Visual Studio 2008. If you’re old school like me, add a reference to /Client-3.5/Raven.Client-3.5.dll – otherwise add a reference to /Client/Raven.Client.Lightweight.dll.

Now, to open a connection, do this:

var documentStore = new DocumentStore {Url = "http://localhost:8080"};
documentStore.Initialize();
 
using (var session = documentStore.OpenSession())
{
    // ....
}

- and then store the DocumentStore as a singleton in your program.

Inserting a document

Now, let’s try inserting a document… say we have a POCO model representation of a person that looks like this (allowing Address to be either DomesticAddress or ForeignAddress):

public class Person
{
    public string Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
}
 
public abstract class Address
{
    public abstract string ToString(string separator);
}
 
public class ForeignAddress : Address
{
    public string[] AddressLines { get; set; }
 
    public override string ToString(string separator)
    {
        return string.Join(separator, AddressLines ?? new string[0]);
    }
}
 
public class DomesticAddress : Address
{
    public string Street { get; set; }
    public string HouseNumber { get; set; }
    public string PostalCode { get; set; }
    public string City { get; set; }
 
    public override string ToString(string separator)
    {
        return string.Join(separator, new[]
                                          {
                                              string.Format("{0} {1}", Street, HouseNumber),
                                              string.Format("{0} {1}", PostalCode, City)
                                          });
    }
}

Then, do this:

using (var session = documentStore.OpenSession())
{
    session.Store(new Person
                      {
                          FirstName = "Mogens Heller",
                          LastName = "Grabe",
                          Address = new DomesticAddress
                                        {
                                            Street = "Torsmark",
                                            HouseNumber = "4",
                                            PostalCode = "8700",
                                            City = "Horsens"
                                        }
                      });
    session.SaveChanges();
}

Now, let’s visit http://localhost:8080/raven/documents.html in the browser… it will probably look something like this:

Document in RavenDB

As you can see, RavenDB stores all documents in a single collection. Right now, there’s one person in there, and then there’s a document that RavenDB uses to generate integer IDs based on the hi-lo-algorithm. Rob Ashton has an explanation here on the design decisions made for this particular piece of RavenDB.

I like this particular decision, because it makes for some really nice human-readable, human-typeable IDs.

Note how the ID of the document is people/1 – RavenDB is smart enough to pluralize most names, which is pretty cool. Let’s click the document to see what’s in it:

Document in RavenDB

Note also how RavenDB puts type information in the document, allowing the proper subtype to be deserialized. Now, let’s try this out:

using (var session = documentStore.OpenSession())
{
    var me = session.Load<Person>("people/1");
    Console.WriteLine(@"{0} {1}
{2}", me.FirstName, me.LastName, me.Address);
}

- which results in the following console output:

Loading document [people/1] from http://localhost:8080
Mogens Heller Grabe
Torsmark 4
8700 Horsens

How cool is that?! (pretty cool, actually…)

Note that the pretty UI is based on the actual RavenDB interface to the world, which is REST-based. That means we can go to a DOS prompt and do this:

C:\>curl -X GET localhost:8080/docs/people/1
{"FirstName":"Mogens Heller","LastName":"Grabe","Address":{"$type":"raventjek.Class1+DomesticAddress, raventjek","Street":"Torsmark","HouseNumber":"4","PostalCode":"8700","City":"Horsens"}}

Now, that was a short dive into storing documents and retrieving them again by ID. We need to do more than that, though – otherwise we would have been content using a simple key/value-store. Therefore, in the next post, I will take a look at querying

Trifork Geek Night moved

October 20th, 2010 by mookid

Just want to say that the planned Trifork Geek Night on “Advanced Windsor-tricks” I mentioned earlier on the 23rd of November is moved to the 13th of December.

Somehow someone double-booked our presentation facilities in Taastrup – sorry, if it has caused inconvenience for you.

To sum it up: Trifork Geek Night on “Advanced Windsor-tricks” on the 13th of December at the Trifork office in Copenhagen.

Book review: NHibernate 3.0 Cookbook

October 8th, 2010 by mookid

NHibernate 3.0 Cookbook coverPackt Publishing has before asked if I was interested in reviewing some of their books, which I was – this time, however, I asked them if they were interested in me reviewing their upcoming NHibernate 3.0 Cookbook by Jason Dentler.

I did that, because I like NHibernate very much, and I would like to help promote good fresh litterature about the subject. And this book really stands out as fresh, because it covers NHibernate 3.0 which has not even been officially released yet!

My first impressions are good – it starts out with creating a model and the usual XML-mapping stuff, and then it dives directly into modeling an inheritance hierarchy. I think this is pretty cool, because it is a sign that the book has a fairly high level of ambition: It is not just about stuffing away rows in the db, it’s about persisting an actual model!

It covers Fluent NHibernate and Fabio’s ConfORM as well, so it provides a really good foundation to anyone interested in learning the intricacies of configuring NHibernate. And it is pretty true to the model-first approach, which is how I like it.

Then it goes on with a chapter on how to manage sessions and transactions including – among other things – an example on how to manage the session from an ASP.NET MVC action filter (which is not “best practice” from an ASP.NET MVC perspective IMO, as it relies on static gateways, but I digress… the book is not about ASP.NET MVC :) )

The query chapter is great, because it covers everything I can think of: Criteria, QueryOver, HQL, both in their normal and multi forms, futures, LINQ to NHibernate, detached criteria, and the new HQL bulk operations, insert and update. If I must put my finger on something, I think that the different areas are covered a little too lightly, but hey – there’s plenty of information on this stuff on the internet, and you could probably write an entire book entirely about how to put HQL to use.

The testing chapter is great as well, as it touches on nice-to-know stuff and some of “the new developments” in the area: NHibernate Profiler, Fluent NHibernate automatic persistence testing, using in-memory SQLite for persistence testing, + more.

The chapter on implementing a data access layer shows a typical data access object and a repository implementation which will probably look familiar to a lot of people, implemented with NHibernate. They both have the ability to automatically perform their operations withing transactions, if one is not already active. This makes the implementations pretty flexible, as they can be used either “by themselves”, or they can implicitly enlist in an ongoing unit of work. Moreover, a pretty nifty named query implementation is shown, complete with automated test that checks whether all implemented named query classes have corresponding named queries in an HBM XML file.

The rest of the book shows how various common tasks can be achieved using NHibernate or some of the many NHContrib projects, like e.g. creating an audit trail by listening to events, creating an IUserType to encrypt strings, using Burrows to manage session, putting NHibernate Search to use, etc… As you can probably imagine, this stuff is covered pretty lightly, but it’s sufficient to give an impression on the huge ecosystem that surrounds NHibernate, which is great.

Conclusion

It strikes me that the book is definitely a “no BS-book” – there’s plenty of code, which is mostly high quality and sufficiently best practice-compliant, and recommendations throughout when there are decisions to be made. If I should criticize something, I think the sheer amount of code makes for an exhausting casual read :) it does, however, claim to be a “cookbook”, so I guess that’s just the way it is.

The book is probably great for developers, who are either new to or semi-experienced in using NHibernate, but have a general high level of experience and skills.

All-in-all a good read, and it’s great that it touches on so many things in and around this huge framework!

Title: NHibernate 3.0 Cookbook
Author: Jason Dentler
ISBN 10/13: 184951304X / 978-1-84951-304-3
Publisher: Packt Publishing

Trifork Geek Nights

September 30th, 2010 by mookid

Just wanted to say that I will be speaking at a couple of Trifork Geek Nights in November and December, about Castle Windsor and NServiceBus respectively.

First,

Advanced Windsor-Tricks

A quick introduction followed by some of the more advanced features of my favorite IoC container. If you’re new to IoC, or you are wondering what the fuzz is about, or you are interested in letting your container do some more work for you, you should come to this one :)

Will be held on

from 4:30 pm to 6:30 pm.

And then,

Distributed systems in .NET with NServiceBus

Introduction to messaging in .NET with NServiceBus. Will give an introduction to some fundamental messaging patterns and go on to show how these can be put to use with NServiceBus.

If you’re new to IoC, you can probably benefit from showing up at the “Advanced Windsor-Tricks” geek night before going to this one, because NServiceBus is relying heavily on having a container.

Will be held on

from 4:30 pm to 6:30 pm as well.

Hope to see some enthusiastic coders there.

PS: The Geek Nights will be held in Danish :)

Shouldly – better assertions

September 21st, 2010 by mookid

Today I came across Shouldly, as I followed a link in a tweet by Rob Conery. I have a thing for nifty mini-projects, so I immediately git clone http://github.com/shouldly/shouldly.git‘d it, and pulled it into a small thing I am building.

What is Shouldly? Basically, it’s just a niftier way of writing assert statements in tests. It fits right in between NUnit and Rhino Mocks, so you will get the most out of it if you are using those two for your unit tests. Check out this repository test – first, arrange and act:

var notes = new Notes{Artist="Josh Rouse", Title="Winter In The Hamptons"};
repo.Save(notes);
var id = notes.Id;
 
var loadedNotes = repo.FindOne(id);

and then, usually the assert would look something like this:

Assert.AreEqual("Joe Rouse", loadedNotes.Artist);
Assert.AreEqual("Winter In The Hamptons", loadedNotes.Title);

- yielding error messages like this:

NUnit.Framework.AssertionException:   Expected string length 9 but was 10. Strings differ at index 2.
  Expected: "Joe Rouse"
  But was:  "Josh Rouse"
  -------------^

which is probably OK and acceptable, because how would NUnit know any better than that?

Check out what Shouldly can do:

loadedNotes.Artist.ShouldBe("Joe Rouse");
loadedNotes.Title.ShouldBe("Winter In The Hamptons");

- yielding error messages like this:

NUnit.Framework.AssertionException: loadedNotes.Artist
        should be
    "Joe Rouse"
        but was
    "Josh Rouse"

which IMO is just too nifty to ignore!

Shouldly takes advantage of the fact the the current StackTrace has all the information we’re after when the assert is an extension method, which is extremely cool and well thought out.

Moreover, it integrates with Rhino Mocks, yielding better messages when doing AssertWasCalled stuff: Instead of just telling that the test did not pass, it tells you exactly which calls were recorded and which one was expected – a thing that Rhino Mocks has always been missing.

Conclusion: YourNextProject.ShouldContain("Shouldly")

Castle Windsor debugger visualization

September 15th, 2010 by mookid

A while ago, I noticed that Krzysztof Ko?mic was tweeting regularly about adding debugger visualization to Windsor. At the time, I wasn’t really paying attention, so I didn’ t actually understand what he was doing.

Today, I was coding some stuff, and I downloaded the latest and greatest Windsor 2.5 (from August 2010), and after a while I found myself stepping through some code in a small web application I was building from scratch – and that’s when I found out what he was rambling about…. check this out:

Windsor debugger visualization

See how the container renders itself as a list of all the components it contains, followed by a list of “Potentially Misconfigured Components”…?

How cool is that? (pretty cool, that’s how cool it is!)

Not a ground-breaking earth-shaking feature on the grand scale of cool stuff, but sometimes it’s the little things…

(I’m puzzled, however, as to why the headline says “Count = 4″ in the “Potentially Misconfigured Components” section when the count is clearly 2…?)

Scheduling recurring tasks in NServiceBus

August 10th, 2010 by mookid

A while ago, on a project I am currently involved with which is based on NServiceBus, we needed to publish certain pieces of information at fixed intervals. I was not totally clear in my head on how this could be implemented in an NServiceBus service, so I asked for help on Twitter, which resulted in a nifty piece of advice from Andreas Öhlund: Set up a timer to do a bus.SendLocal at the specified interval.

That’s exactly what we did, and I think we ended up with a pretty nifty piece of code that I want to show off :)

PS: bus.SendLocal(message) effectively does a bus.Send(((UnicastBus)bus).Address, message) – i.e. it puts a message, MSMQ and all, in the service’s own input queue.

First, we have an API that looks like this (looking a little funny, I know – wait and see…):

public interface ISchedule
{
    void Every(TimeSpan interval, Func<IMessage> messageFactoryMethod);
}

- which is implemented like this (registered as a singleton in the container):

public class ServerBasedTimerSchedule : ISchedule, IDisposable
{
    readonly IBus bus;
    readonly List<System.Timers.Timer> timers = new List<System.Timers.Timer>();
 
    public ServerBasedTimerSchedule(IBus bus)
    {
        this.bus = bus;
    }
 
    public void Every(TimeSpan interval, Func<IMessage> messageFactoryMethod)
    {
        var timer = new System.Timers.Timer();
        timer.Elapsed += (_, __) => bus.SendLocal(messageFactoryMethod());
        timer.Interval = interval.TotalMilliseconds;
        timer.Start();
        timers.Add(timer);
    }
 
    public void Dispose()
    {
        timers.ForEach(timer => timer.Dispose());
    }
}

The System.Timers.Timer is a timer which uses the thread pool to schedule callbacks at the specified interval. It’s pretty easy to use, and it fits nicely with this scenario.

Now, in combination with this nifty class of extension goodness:

public static class TimeSpanExtensions
{
    public static TimeSpan Seconds(this int seconds)
    {
        return TimeSpan.FromSeconds(seconds);
    }
 
    public static TimeSpan Minutes(this int minutes)
    {
        return TimeSpan.FromMinutes(minutes);
    }
 
    // ... etc + for doubles as well
}

- we can schedule our tasks like so:

public class ScheduleRealTimeDataPublishing : IWantToRunAtStartup
{
    public ScheduleRealTimeDataPublishing(ISchedule schedule)
    {
        this.schedule = schedule;
    }
 
    public void Run()
    {
        schedule.Every(5.Seconds(), () => new PublishRealTimeDataMessage());
    }
 
    public void Stop()
    {
    }
}

Now, why is this good? It’s good because the actual task will then be carried out by whoever implements IHandleMessages<PublishRealTimeDataMessage> in the service, processing the tasks with all the benefits of the usual NServiceBus message processing pipeline.

Nifty, huh?

Looking over the simplicity and elegance of this solution, I’m kind of embarassed to tell that my first take on this was to implement the timer almost exactly like above, except instead of bus.SendLocal in the Elapsed-callback, we had a huge event handler that simulated most of our message processing pipeline – including NHibernateMessageModule, transactions, and whatnot….

Please note that ScheduleRealTimeDataPublishing is not re-entrant – in this form its Every method should only be used from within the Run and Stop methods of implementors of IWantToRunAtStartup, as these are run sequentially.

« Previous Entries Next Entries »