The Importance of Parameter Names in RabbitMQ / EasyNetQ

I've started using EasyNetQ as a client for a messaging system I'm writing using RabbitMQ as the messaging engine. I'm following a CQRS design pattern with part of my system publishing commands and command handlers subscribing to those commands. The system could, then, raise events which get published out and are subscribed to event handlers. EasyNetQ is being used, pretty much out of the box, as my C# API into RabbitMQ.

All is working fine - in fact, it's quite nice. However, I caused a small issue because I didn't fully understand how EasyNetQ deserializes messages into C# types.

A typical command that I'm issuing consists of a handful of publicly "get-able" properties with private setters. My command constructor takes in the properties and sets the properties - so my command is mostly read-only.

public class DoSomeWork : ICommand
{
  public DoSomeWork(Int32 id, String period, MyDatabaseTypeEnum db)
  {
    Id = id;
    Period = period;
    Database = db;
  }

  public Int32 Id { get; private set; }
  public String Period { get; private set; }
  public MyDatabaseTypeEnum Database { get; private set; }
}

Pretty simple, right? However, when I would publish this command into RabbitMQ, my subscriber would receive the command but the Database property would be a default 0 value. The MyDatabaseTypeEnum has a bunch of different values, and regardless of the value I would set in my command's constructor, it would always emerge to the subscriber as a 0.

My message, in RabbitMQ, looked something like this:

{ "id": 100, "period": "1Q2014", "database": 100 }

This was expected - my message was being serialized perfectly and the MyDatabaseTypeEnum value was persisted as its underlying Int32 value (100 in this case).

Being used to how tools like MongoDB and Web API serialize and deserialize, I expected EasyNetQ to deserialize this message to the appropriate properties, even though they were private setters. (My other commands were deserializing fine, by the way.) But this one wasn't deserializing the enum. The reason why was basically a dumb error on my part.

Instead of using reflection to set the properties (as I expected), EasyNetQ attempts to call my constructor. It lines up message property names to constructor parameter names. So id and period lined up perfectly fine; but db didn't have a corresponding message property. Being a value type, it just ended up with the default value for an Int32 -- 0. If it were a string or a reference type, its default value would have been null.

A simple change to my command constructor and all worked well:

public class DoSomeWork : ICommand
{
  public DoSomeWork(Int32 id, String period, MyDatabaseTypeEnum database)
  {
    Id = id;
    Period = period;
    Database = database;
  }

  public Int32 Id { get; private set; }
  public String Period { get; private set; }
  public MyDatabaseTypeEnum Database { get; private set; }
}

My system is working like a charm again!

So, the rule is -- make sure your constructor parameters line up with your message property names. If you're working with C# types, make sure the properties that get serialized have a corresponding constructor parameter name that differs only in casing.