Tuesday, February 25, 2014

EasyNetQ: Client Details in Connection String

From version 0.27.3 of EasyNetQ, you can set your client product name and platform in the connection string:

var bus = RabbitHutch.CreateBus("host=localhost;product=pdf.render;platform=snowball");

These will then appear in the RabbitMQ Management UI connection list under the Client column:

ManagementUI_Client_Column

Underneath is the EasyNetQ version number.

If you don’t specify product or platform, the product is shown as the name of your executable, and the platform is the host name.

Tuesday, February 04, 2014

EasyNetQ: A Layered API

I had a great discussion today on the EasyNetQ mailing list about a pull request. It forced me to articulate how I view the EasyNetQ API as being made up of distinct layers, each with a different purpose.

EasyNetQ_API

EasyNetQ is a collection of components that provide services on top of the RabbitMQ.Client library. These do things like serialization, error handling, thread marshalling, connection management, etc. They are composed by a mini-IoC container. You can replace any component with your own implementation quite easily. So if you’d like XML serialization instead of the built in JSON, just write an implementation of ISerializer and register it with the container.

These components are fronted by the IAdvancedBus API. This looks a lot like the AMQP specification, and indeed you can run most AMQP methods from this API. The only AMQP concept that this API hides from you is channels. This is because channels are a confusing low-level concept that should never have been part of the AMQP specification in the first place. ‘Advanced’ is not a very good name for this API to be honest, ‘Iamqp’ would be much better.

Layered on top of the advanced API are a set of messaging patterns: Publish/Subscribe, Request/Response, and Send/Receive. This is the ‘opinionated’ part of EasyNetQ. It is our take on how such patterns should be implemented. There is very little flexibility; either you accept our way of doing things, or you don’t use it. The intention is that you, the user, don’t have to expend mental bandwidth re-inventing the same patterns; you don’t have to make choices every time you simply want to publish a message and subscribe to it. It’s designed to achieve EasyNetQ’s core goal of making working with RabbitMQ as easy as possible.

The patterns sit behind the IBus API. Once again, this is a poor name, it’s got very little to do with the concept of a message bus. A better name would be IPackagedMessagePatterns.

IBus is intended to work for 80% of users, 80% of the time. It’s not exhaustive. If the pattern you want to implement is not provided by IBus, then you should use IAdvancedBus. There’s no problem with doing this, and it’s how EasyNetQ is designed to be used.

I hope this explains the design philosophy behind EasyNetQ and why I push back against pull requests that add complexity to the IBus API. I see the ease-of-use aspect of EasyNetQ as its most important attribute. RabbitMQ is a superb piece of infrastructure and I want as many people in the .NET community to use it as possible.

Thursday, January 30, 2014

EasyNetQ: Publishing Non-Persistent Messages

logo_design_150

In AMQP, buried in the basic.properties object that gets sent along with each published message, there is a delivery_mode setting. You can set it to either ‘persistent’ (1), or ‘non-persistent’ (2). It controls whether a message is persisted to disk or not. In the AMQP spec:

“The server SHOULD respect the persistent property of basic messages and SHOULD make a best-effort to hold persistent basic messages on a reliable storage mechanism.”

Of course it’s pointless setting delivery_mode to ‘persistent’ if you’re not publishing to a durable queue.

By default EasyNetQ sets delivery_mode to persistent (1) when calling IBus.Publish. We make the assumption that people would want this safe behaviour out-of-the-box. However, it does introduce a performance hit, so if you don’t care about losing messages in the case of a server restart you should be able to change this behaviour.

From version 0.26.3, EasyNetQ has a new boolean connection string parameter ‘persistentMessages’. By default it is set to true, but if you don’t need persistent messages, but do need high performance, set it to false:

vas bus = RabbitHutch.CreateBus("host=localhost;persistentMessages=false");

This setting has no effect on the advanced API (IAdvancedBus) where you have access to basic.properties and are free to set delivery_mode on a message by message basis.

Tuesday, January 21, 2014

In Praise of TestDriven.NET

I’ve been using TestDriven.NET by Jamie Cansdale for quite a few years now. Ostensibly it’s a unit test runner, but that is not the real reason why you should use it. The killer feature, the one that will give you developer super powers, is the ability to run any arbitrary code under the cursor.

TestDriven allows you to run individual unit tests simply by placing the cursor within the test method and running the command ‘RunTests’ (I have this mapped to the F8 key - who uses bookmarks anyway?) The really cool thing is that the method doesn’t need to be attributed as a unit test, it can be any arbitrary method. Any return value from the method is written to the Visual Studio output console, as are any Console.WriteLine() or Console.Write() statements. This gives you immediate feedback on any code with a single keystroke.

 TestDriven

Why is this awesome? The key to productive software development is reducing latency; the cycle time between an action and its results. That’s why continuous integration and continuous delivery are such huge wins. When you’re coding, the quicker you can try a quick experiment and see the results, the more productive you will be. The big problem with compiled languages like C, C++, Java, and C# is that the compilation cycle acts as huge barrier to iteration. I still remember with horror how I used to write some code, launch my application, navigate to where the feature would be exercised (taking care to enter the correct parameters of course) and then watch it fail. And then I’d repeat the same tedious cycle over and over again. That’s one of the main reasons why .NET developers spend so much time stepping through code in the (admittedly excellent) debugger, because it’s hard otherwise to know how the last few lines of code you wrote are executing. Now I run code continuously without launching anything. Simply write a function, F8, iterate. It’s so much more productive.

I know fellow developers who use other tools as a scratchpad for iterative experiments. LinqPad is very popular and ScriptCS is deservedly getting a lot of attention. But TestDriven has a huge advantage over these because it works inside your Visual Studio environment. There’s no need to copy and paste your experiment into your application, you iterate in place and in the context of your existing code.

Give it a try. I’ve found it a game  changer for C# development. It’s a pretty good test runner too.

Thursday, January 02, 2014

The Geek Christmas Quiz 2013: The Answers!

Here are the answers to this year’s Geek Christmas Quiz.

What does this acronym stand for? (one point per correct answer)
    1. AWS – Amazon Web Services
    2. SQL – Structured Query Language
    3. NATO – North Atlantic Treaty Organization
    4. GNU – GNU is Not Unix
    5. SCSI – Small Computer Serial Interface
    6. HTML – Hyper Text Markup Language
    7. HTTP – Hyper Text Transfer Protocol
    8. ReST – Representational State Transfer
    9. NASA – National Aeronautics and Space Administration
    10. RAF – Royal Air Force

Name that computer (one point for the model, another for the company/inventor)

NeXT Cube / NeXT Apple II / Apple Difference Engine / Babbage
IBM PC / IBM Macintosh / Apple ZX Spectrum / Sinclair
System 360 / IBM Colossus / Tommy Flowers PlayStation / Sony

Name that programming language (one point for each correct answer)

  1. BASIC
  2. C#
  3. C
  4. F#
  5. Python
  6. T-SQL
  7. Bash
  8. Lisp/Scheme/Clojure
  9. Haskell

Science fiction (one point for each correct answer)

    1. "These are not the droids you are looking for" (what film?) – Start Wars IV
    2. "Open the pod bay doors please HAL" (what film, or book?) – 2001 A Space Odyssey
    3. Who wrote Rendezvous with Rama? – Arthur C Clarke
    4. What spaceship uses dilithium crystals – USS Enterprise
    5. what does the acronym CREW in Iain Bank's culture novels mean when referring to weapons - Coherent Radiation Emission Weapon
    6. Name 3 futurama characters - http://en.wikipedia.org/wiki/List_of_Futurama_characters
    7. Who wrote the 3 laws of robotics? – Isaac Asimov
    8. What is the first law of robotics? – “A robot may not injure a human being or, through inaction, allow a human being to come to harm”
    9. Who is the leader of the Daleks? - Davros
    10. Directive? (which film?) – Wall-E

Science (one point for each correct answer)

    1. 1 degree fahrenheit is a different size to 1 degree centigrade which means they must cross at some point. At what temperature are both scales the same? - -40.
    2. When and where was the first cell in your body created? - The egg that created you was formed inside of your mother’s fetus while she was inside of your grandmother’s womb.
    3. What is the device which blends air and fuel in an internal combustion engine called? – Carburettor.
    4. What was the name of the first hydrogen (thermonuclear) bomb test? – Mike.
    5. What is special about Sirius, the Dog Star? – It is the brightest star in the night sky.
    6. What year did the last man land on the moon? – 1972 (Apollo 17)
    7. Who invented the jet engine? – Frank Whittle.
    8. In trigonometry what is calculated by dividing the adjacent by the hypotenuse? - Cosine
    9. Which part of the Earth lies between the outer core and the crust? – The mantle.
    10. Where in the body are alveoli to be found? – The lungs.

Name that cartoon character (one point for each correct answer)

Lisa (Simpsons) Iron Man Fone Bone
Denis the Menace Tom & Jerry Kiki (Kiki’s Delivery Service)
Pointy Haired Boss Professor Calculus Dan Dare

Friday, December 20, 2013

The Geek Christmas Quiz 2013

This is the second year of my Geek Christmas Quiz. Six sections of the ultimate geek questions. The office competition was won by Toby Carter with a score of 46. See if you can do better.

The answers are here.

Last year’s quiz is here.

What does this acronym stand for? (one point per correct answer)
    1. AWS
    2. SQL
    3. NATO
    4. GNU
    5. SCSI
    6. HTML
    7. HTTP
    8. ReST
    9. NASA
    10. RAF

Name that computer (one point for the model, another for the company/inventor)

Quiz_name_that_computer

Name that programming language (one point for each correct answer)

1.    
10 PRINT "HELLO WORLD"
20 GOTO 10 

2.    
while(true)
{
    Console.WriteLine("Hello World");
} 

3.    
for(;;) {
    printf("Hello World");
} 

4.    
let rec hello () =
    printfn "Hello World"
    hello() 

5.    
while true
    writeln ("Hello World") 

6.    
WHILE 1 = 1 PRINT "Hello World" 

7.    
while /bin/true; do echo -n 'Hello World!'; done; 

8.    
(while true (print "hello world")) 

9.    
let hello = do putStr "Hello World"; hello 

Science fiction (one point for each correct answer)

    1. "These are not the droids you are looking for" (what film?)
    2. "Open the pod bay doors please HAL" (what film, or book?)
    3. Who wrote Rendezvous with Rama?
    4. What spaceship uses dilithium crystals
    5. what does the acronym CREW in Iain Bank's culture novels mean when referring to weapons
    6. Name 3 futurama characters
    7. Who wrote the 3 laws of robotics?
    8. What is the first law of robotics?
    9. Who is the leader of the Daleks?
    10. Directive? (which film?)

Science (one point for each correct answer)

    1. 1 degree fahrenheit is a different size to 1 degree centigrade which means they must cross at some point. At what temperature are both scales the same?
    2. When and where was the first cell in your body created?
    3. What is the device which blends air and fuel in an internal combustion engine called?
    4. What was the name of the first hydrogen (thermonuclear) bomb test?
    5. What is special about Sirius, the Dog Star?
    6. What year did the last man land on the moon?
    7. Who invented the jet engine?
    8. In trigonometry what is calculated by dividing the adjacent over the hypotenuse?
    9. Which part of the Earth lies between the outer core and the crust?
    10. Where in the body are alveoli to be found?

Name that cartoon character (one point for each correct answer)

Quiz_name_that_cartoon_character

Tuesday, December 17, 2013

How to Run a Successful Open Source Project

A couple of months ago I attended BarCamp Brighton, an open conference at Brighton University. Everyone is encouraged to present a session, and as I don’t need much excuse to talk to a room full of people I thought I’d do an unrehearsed talk on running an open source project based on my experiences with EasyNetQ. I spent about half an hour going through what EasyNetQ is, its history, and how I organise things, to a room of six people. I know, fame at last! The really interesting bit came during the questions when I got into a discussion with a chap who worked for the Mozilla foundation. He asked what makes a successful open source project. Of course ‘success’ is very subjective. Success for me means that some people are interested enough in EasyNetQ to download it from NuGet and commit time to sending me bug reports and pull requests, for other people success might be defined as a project so popular that they can make a living from supporting it. Given the former, lower bar, here are some of the things that we came up with.

  • It has to do something useful. Obvious, yes. The classic ‘scratching an itch’ reason for starting an OSS project usually means that it’s useful to you. How successful your project will be depends on how common your problem is.
  • A clear mission. If you have a clear mission, it’s easy for people to understand the problem you are trying to solve. EasyNetQ’s mission is to provide the simplest possible API for RabbitMQ on .NET. People know what the intention is, and it provides a guide to EasyNetQ’s design.
  • Easy to install. If your environment provides a package manager, make sure your project provides a package for it. Otherwise make sure you provide a simple installer and clear instructions. Don’t insist that people build your project from source if you can avoid it. EasyNetQ installs from NuGet in a few seconds.
  • A good ‘first 20 minutes’ experience. Make your project super easy to get started with. Have a quick start guide to help users get something working as soon as possible. You can get EasyNetQ up and running with two console apps publishing and subscribing within 10 minutes. Hmm, I need to get that quick start guide done though :p
  • Documentation! Clear, simple to follow, documentation is really helpful to your users. Keep it up to date. A wiki that anyone can update is ideal. I use the GitHub wiki for EasyNetQ’s documentation. If you can register a domain name and have a nice homepage with a short pithy introduction to your project, so much the better.
  • A forum for communicating with your users. I use a Google group. But there are many other options. Make sure you answer people’s questions. Be nice (see below).
  • Let people know what’s happening. A blog, like this, is an ideal place to write about the latest project news. Let people know about developments and your plans for the future.
  • Release early, release often. Long feedback loops kill software. That’s true of any project, open or closed, but much more so for open source. The sooner you get your code in front of people, the sooner you find out when it isn’t working. Have a continuous build process that allows people to be able to quickly install the very latest version. You can split out ‘stable’ from ‘development’ releases if you want. I don’t bother, every commit to EasyNetQ’s GitHub repository is immediately published to NuGet, but that will probably change as the project matures.
  • Have a versioning policy and explain it. You can see EasyNetQ’s here.
  • Use GitHub to host your source code. Yes, yes, I know there are alternatives, but GitHub has made hosting and contributing to OSS projects much easier. It has become so ubiquitous that it’s the obvious choice. Everyone knows how it works.
  • Be nice! It’s easy to get frustrated with weird questions on the mailing list, or strange pull requests, but always remember to be polite and helpful. Be especially nice to people who send you bug reports and pull requests, they are helping your project for no gain. Be thankful.

I’m sure there are loads of other things that I’ll think of as soon as I hit ‘publish’ on this post, so don’t think of this as comprehensive. I’d love to hear any more suggestions in the comments.

Of course the main problem most people have to overcome with running an OSS project is finding the time. I’ve been extraordinarily fortunate that 15below have sponsored EasyNetQ. If you use it and like it, don’t forget to thank them.

Thursday, December 12, 2013

Are Your Programmers Working Hard, Or Are They Lazy?

When people are doing a physical task, it’s easy to assess how hard they are working. You can see the physical movement, the sweat. You also see the result of their work: the brick wall rising, the hole in the ground getting bigger. Recognising and rewarding hard work is a pretty fundamental human instinct, it is one of the reasons we find endurance sports so fascinating. This instinctive appreciation of physical hard work is a problem when it comes to managing creative-technical employees. Effective knowledge workers often don’t look like they are working very hard.

Back in 2004, I was a junior developer working in a large team on a cable TV company’s billing and provisioning system. Like all large systems it was made up of a number of relatively independent components, with different individuals or small teams looking after them. The analogue TV and digital TV provisioning systems were almost entirely separate, with a different team looking after each.

The analogue TV team had decided to base their system around an early version of Microsoft Biztalk. There were four of our guys and a team from Microsoft developing it and running it in production. They all appeared to work really hard. They would often be seen working into the night and at weekends. Everyone would drop what they were doing to help with production issues, often crowding around a single guy at a desk, offering suggestions about what could be wrong, or how to fix something. There was constant activity, and anyone could see, just by looking that, not only did everyone pull together as a team, but they were all working really really hard.

The digital TV provisioning team was very different. The code had been mostly written by a single guy, let’s call him Dave. I was a junior maintenance developer on the team. Initially I had a great deal of trouble understanding the code. There wasn’t one long procedure somewhere where all the stuff happened, instead there were lots of small classes and methods with just a few lines of code. Several of my colleagues complained that Dave made things overcomplicated. But Dave took me under his wing and suggested that I read a few books on object oriented programming. He taught me about design patterns, the SOLID principles, and unit testing. Soon the code started to make sense, and the more I worked on it the more I came to appreciated its elegant design. It didn’t go wrong in production, just hummed away doing its job. It was relatively easy to make changes to the code too, so implementing new features was often quite painless. The unit tests meant that few bugs made it into production.

The result of all this was that it didn’t look like we were working very hard at all. I went home at 5.30pm, I never worked weekends, we didn’t spend hours crowded around each other’s desks throwing out guesses about what could be wrong with some failing production system. From the outside it must have looked like we’d been given a far easier task than the analogue TV guys. In truth, the requirements were very similar, we just had better designed and implemented software, and better supporting infrastructure, especially the unit tests.

Management announced that they were going to give out pay rises based on performance. When it was my turn to talk to the boss, he explained that it was only fair that the pay increases went to the people who worked really hard, and that our team just didn’t seem to care so much about the company, not compared to the heroes who gave up their evenings and weekends.

The cable company was a rare laboratory, you could observe a direct comparison between the effects of good and bad software design and team behaviour. Most organisations don’t provide such a comparison. It’s very hard to tell if that guy sweating away, working late nights and weekends, constantly fire-fighting, is showing great commitment to making a really really complex system work, or is just failing. Unless you can afford to have two or more competing teams solving the same problem, and c’mon, who would do that, you will never know. Conversely, what about the guy sitting in the corner who works 9 to 5, and seems to spend a lot of time reading the internet? Is he just very proficient at writing stable reliable code, or is his job just easier than everyone else’s? To the casual observer, the first chap is working really hard, the second one isn’t. Hard work is good, laziness is bad, surely?

I would submit that the appearance of hard work is often an indication of failure. Software development often isn’t done well in a pressurised, interrupt driven, environment. It’s often not a good idea to work long hours. Sometimes the best way of solving a difficult problem is to stop thinking about it, go for a walk, or even better, get a good night’s sleep and let your subconscious solve it. One of my favourite books is A Mathematician’s Apology by G. H. Hardy, one of the leading British mathematicians of the 20th century. In it he describes his daily routine: four hours work in the morning followed by an afternoon of watching cricket. He says that it’s pointless and unproductive to do hard mental work for more than four hours a day.

To managers I would say, judge people by results, by working software, not by how hard they appear to be working. Counter intuitively, it may be better not to sit with your developers, you may get a better idea of their output unaffected by conventional/intuitive indicators. Remote working is especially beneficial; you will have to measure your employees by their output, rather than the lazier option of watching them sitting at their desks 8 hours a day thumping away at an IDE, or ‘helpfully’ crowding around each other’s desks offering ‘useful’ suggestions.

Monday, December 09, 2013

EasyNetQ: Replace the Internal DI Container

logo_design_150

EasyNetQ, is made up of a collection of independent components. Internally it uses a tiny internal DI (IoC) container called DefaultServiceProvider. If you look at the code for the static RabbitHutch class that you use to create instances of the core IBus interface, you will see that it simply creates a new DefaultServiceProvider, registers all of EasyNetQ’s components, and then calls container.Resolve<IBus>() creating a new instance of IBus with its tree of dependencies supplied by the container:

public static IBus CreateBus(IConnectionConfiguration connectionConfiguration, Action<IServiceRegister> registerServices)
{
Preconditions.CheckNotNull(connectionConfiguration, "connectionConfiguration");
Preconditions.CheckNotNull(registerServices, "registerServices");

var container = createContainerInternal();
if (container == null)
{
throw new EasyNetQException("Could not create container. " +
"Have you called SetContainerFactory(...) with a function that returns null?");
}

registerServices(container);
container.Register(_ => connectionConfiguration);
ComponentRegistration.RegisterServices(container);

return container.Resolve<IBus>();
}

But what if you want EasyNetQ to use your container of choice? From version 0.25 the RabbitHutch class provides a static method, SetContainerFactory, that allows you to register an alternative container factory method that provides whatever implementation of EasyNetQ.IContainer that you care to supply.

In this example we are using the Castle Windsor IoC container:

// register our alternative container factory
RabbitHutch.SetContainerFactory(() =>
{
// create an instance of Windsor
var windsorContainer = new WindsorContainer();
// wrap it in our implementation of EasyNetQ.IContainer
return new WindsorContainerWrapper(windsorContainer);
});
// now we can create an IBus instance, but it's resolved from
// windsor, rather than EasyNetQ's default service provider.
var bus = RabbitHutch.CreateBus("host=localhost");

Here is how I implemented WindsorContainerWrapper:

public class WindsorContainerWrapper : IContainer, IDisposable
{
private readonly IWindsorContainer windsorContainer;

public WindsorContainerWrapper(IWindsorContainer windsorContainer)
{
this.windsorContainer = windsorContainer;
}

public TService Resolve<TService>() where TService : class
{
return windsorContainer.Resolve<TService>();
}

public IServiceRegister Register<TService>(System.Func<IServiceProvider, TService> serviceCreator)
where TService : class
{
windsorContainer.Register(
Component.For<TService>().UsingFactoryMethod(() => serviceCreator(this)).LifeStyle.Singleton
);
return this;
}

public IServiceRegister Register<TService, TImplementation>()
where TService : class
where TImplementation : class, TService
{
windsorContainer.Register(
Component.For<TService>().ImplementedBy<TImplementation>().LifeStyle.Singleton
);
return this;
}

public void Dispose()
{
windsorContainer.Dispose();
}
}

Note that all EasyNetQ services should be registered as singletons.

It’s important that you dispose of Windsor correctly. EasyNetQ doesn’t provide a Dispose method on IContainer, but you can access the container via the advanced bus (yes, this is new too), and dispose of windsor that way:

((WindsorContainerWrapper)bus.Advanced.Container).Dispose();
bus.Dispose();

Happy containerisation!

Monday, November 25, 2013

EasyNetQ’s Minimalist DI Container

I’ve been a long time fan of IoC (or DI) containers ever since I first discovered Castle Windsor back in 2007. I’ve used Windsor in every major project I’ve been involved in since then, and if you’d gone to a developer event in the late naughties, you may well have encountered me speaking about Windsor. Indeed, Seb Lambla had the cheek to call me ‘Windsor man’. I shall label him ‘Rest-a-man’ in revenge.

When I started working on EasyNetQ in 2011, I initially thought it would be a very simple lightweight wrapper around the RabbitMQ.Client library. The initial versions were very procedural, ‘just get it done’, script-ish code burps. But as it turned into a more serious library, I started to factor the different pieces into more SRPish classes using dependency injection. At this point I was doing poor-man’s dependency injection, with an initial piece of wire-up code in the RabbitHutch class.

As EasyNetQ started to gain some traction outside 15below, I began to get questions like, “how do I use a different serializer?” And “I don’t like your error handling strategy, how can I implement my own?” I was also starting to get quite bored of maintaining the ever-growing wire up code. The obvious solution was to introduce a DI container, but I was very reluctant to take a dependency on something like Windsor. Writing a library is a very different proposition than writing an application. Every dependency you introduce is a dependency that your user also has to take. Imagine you are a happily using AutoFac and suddenly Castle.Windsor appears in your code base, or even worse, you are an up-to-date Windsor user, but EasyNetQ insists on installing an old version of Windsor alongside. Nasty. Windsor is an amazing library, some of its capabilities are quite magical, but I didn’t need any of these advanced features in EasyNetQ. In fact I could be highly constrained in my DI container requirements:

  • I only needed singleton instances.
  • The lifetime of all DI provided components matches the lifetime of the main IBus interface.
  • I didn’t need the container to manage component disposal.
  • I didn’t need open generic type registration.
  • I was happy to explicitly register all my components, so I didn’t need convention based registration.
  • I only needed constructor injection.
  • I can guarantee that a component implementation will only have a single constructor.

With this highly simplified list of requirements, I realised that I could write a very simple DI container in just few lines of code (currently 113 as it turns out).

My super simple container only provides two registration methods. The first takes a service interface type and a instance creation function:

IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class;

The second takes an instance type and an implementation type:

IServiceRegister Register<TService, TImplementation>()
where TService : class
where TImplementation : class, TService;

These are both defined in a IServiceRegister interface. There is also a single Resolve method in an IServiceProvider interface:

TService Resolve<TService>() where TService : class;

You can see all 113 lines of the implementation in the DefaultServiceProvider class. As you can see, it’s not at all complicated, just three dictionaries, one holding the list of service factories, another holding a list of registrations, and the last a list of instances. Each registration simply adds a record to the instances dictionary. When Resolve is called, a bit of reflection looks up the implementation type’s constructor and invokes it, recursively calling resolve for each constructor argument. If the service is provided by a service factory, it is invoked instead of the constructor.

I didn’t have any worries about performance. The registration and resolve code is only called once when a new instance of IBus is created. EasyNetQ is designed to be instantiated at application start-up and for that instance to last the lifetime of the application. For 90% of applications, you should only need a single IBus instance.

EasyNetQ’s component are registered in a ComponentRegistration class. This provides an opportunity for the user to register services ahead of the default registration, and since the first to register wins, it provides an easy path for users to replace default services implementations with their own. Here’s an example of replacing EasyNetQ’s default console logger with a custom logger:

var logger = new MyLogger();
var bus = RabbitHutch.CreateBus(connectionString, x => x.Register<IEasyNetQLogger>(_ => logger));

You can replace pretty much any of the internals of EasyNetQ in this way: the logger, the serializer, the error handling strategy, the dispatcher threading model … Check out the ComponentRegistration class to see the whole list.

Of course the magic of DI containers, the first rule of their use, and the thing that some people find extraordinarily hard to grok, is that I only need one call to Resolve in the entire EasyNetQ code base at line 146 in the RabbitHutch class:

return serviceProvider.Resolve<IBus>();

IoC/DI containers are often seen as very heavyweight beasts that only enterprise architecture astronauts could love, but nothing could be more wrong. A simple implementation only needs a few lines of code, but provides your application, or library, with considerable flexibility.

EasyNetQ is open source under the (very flexible) MIT licence. Feel free to cut-n-paste my DefaultServiceProvider class for your own use.