C# – DI/IoC, NHibernate and help in getting them to work together

cdependency-injectioninversion-of-controlnhibernate

I'm trying to get my head around DI/IoC, NHibernate and getting them to work nicely together for an application that i'm developing. I'm quite new to both NHibernate and DI/IoC so not quite sure whether what i'm doing is the sensible way to be going about it. This is the scenario:

The application provides users with the ability to calculate a particular value (known as the margin) for a particular financial transaction. The calculation of the marging value for each transaction is carried out by concrete implementations of an abstract MarginCalculator class and the concrete implementation to be used depends on the type of the product for the particular transaction (given by a certain field of the product object). The concrete calculator class is accessed via a property on the product class. i.e.

public class Transaction
{
    private double _margin;
    private Product _product;
    private Client _client;

    public double Margin { get; }
    public Product Product { get; }
    public Client Client { get; }

    public Transaction(Product p, Client c)
    {
        _product = p;
        _client = c;
    }

    public void CalculateMargin()
    {
        _margin = _product.MarginCalculator.CalculateMargin();
    }
}

public class Product
{
    private string _id;
    private string _productType;
    ... Other fields

    public string Id { get; }
    public string ProductType { get; }
    public MarginCalculator MarginCalculator
    {
        get { return MarginCalculatorAssembler.Instance.CreateMarginCalculatorFor(this.ProductType); }
    }
}

public class MarginCalculatorAssembler
{
    public static readonly MarginCalculatorAssembler Instance = new MarginCalculatorAssembler();

    private MarginCalculatorAssembler ()
    {
    }

    public MarginCalculator CreateMarginCalculatorFor(string productType)
    {
        switch (productType)
        {
            case "A":
                return new ConcreteMarginCalculatorA();
            case "B":
                return new ConcreteMarginCalculatorB();
            default:
                throw new ArgumentException();
        }
    }
}

public abstract class MarginCalculator
{
    public abstract double CalculateMargin();
}

public class ConcreteMarginCalculatorA : MarginCalculator
{
    public override double CalculateMargin
    {
        // Perform actual calculation
    }
}

public class ConcreteMarginCalculatorB : MarginCalculator
{
    public override double CalculateMargin
    {
        // Perform actual calculation
    }
}

Users select a particular client and Product from dropdowns and the corresponding clientId and productId are passed to repositories that then use NHibernate to populate product and client objects before they're injected into the transaction object. In my current setup the Transaction receives its Product and Client dependencies via constructor dependency injection (no IoC container used as yet) i.e.

public class ProductRepository : IRepository<Product>
{
    public Product GetById(string id)
    {
        using (ISession session = NHibernateHelper.OpenSession())
            return session.Get<Product>(id);
    }
}

/* Similar repository for Clients */

IRepository<Client> clientRepository = new ClientRepository();
IRepository<Product> productRepository = new ProductRepository();
Client c = clientRepository.GetById(clientId);
Product p = productRepository.GetById(productId);

Transaction t = new Transaction(p, c);

The following are what i'm hoping to get ideas on:

A. Is it considered OK to be accessing the MarginCalculator (which is essentially a service) through the Product domain object or should, as suggested here, (http://stackoverflow.com/questions/340461/dependency-injection-with-nhibernate-objects) the code be restructured so as to remove service dependencies from the domain objects and instead create a new TransactionProcessor class that takes the abstract MarginCalculator as a dependency (along the lines of what's described here (http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/03/31/ptom-the-dependency-inversion-principle.aspx) i.e.

public class TransactionProcessor
{
    private readonly MarginCalculator _marginCalculator;

    public TransactionProcessor(MarginCalculator marginCalculator)
    {
        _marginCalculator = marginCalculator;
    }

    public double CalculateMargin(Transaction t)
    {
        return _marginCalculator.CalculateMargin(Transaction t);
    }
}

public abstract class MarginCalculator
{
    public abstract double CalculateMargin(Transaction t);
}

B. Is it possible to use an IoC Container to get a Transaction object with NHibernate populated/generated Product and Client dependencies injected? i.e. Given a productId and clientId, both provided by the user, is it possible to have something like:

// pseudocode
Transaction t = IoC.Resolve<Transaction>(productId, clientId);

such that the container resolves the Product and Client dependencies of the Transaction object, NHibernate is utilised to populate the Product and Client based on the productId and clientId and then the populated Product and Client are injected into the Transaction?

C. In a typical DI scenario, if class A has a dependency on interface B then the following might be done:

IInterfaceB b = new ClassB();
A a = new A(b);

interface IInterfaceB
{
}

class B : IInterfaceB
{
}

public class A
{
    private IIntefaceB _b;

    public A(IInterfaceB b)
    {
        _b = b;
    }
}

However, this, which is virtually how all examples of DI are shown, assumes that the implementor of IInterfaceB (in this case Class B) is known at design time. Is there a way to use DI in such a way that the implementor is determined at runtime?

Many thanks

Matthew

Best Answer

A) If you're going to access the MarginCalculator through the Product domain object, you might as well cut out the middle man and let the DI/IOC container inject the MarginCalculator for you. You can even get rid of the MarginCalculatorAssembler because most DI/IOC containers do most of the boilerplate code of object construction for you.

B and C) It's very possible. In fact, here's how your code would look like if you used LinFu:


// No need to change the Transaction class
public class Transaction
{
    private double _margin;
    private Product _product;
    private Client _client;

    public double Margin { get; }
    public Product Product { get; }
    public Client Client { get; }

    public Transaction(Product p, Client c)
    {
        _product = p;
        _client = c;
    }

    public void CalculateMargin()
    {
        _margin = _product.MarginCalculator.CalculateMargin();
    }
}

It would be nice if you could get a DI/IOC to inject the product and client instances into the constructor--but before we do that, you need to register the dependencies with the container. Here's how you do it with LinFu.IOC:

// Next, you'd have to tell LinFu to automatically register your product class:
[Factory(typeof(Product))]
public class ProductFactory : IFactory
{
     object CreateInstance(IServiceRequest request)
     {
          // Grab a copy of the IRepository from the container
          var repository = container.GetService>();

          // Get the id (this assumes that your id is an Int32)
          var id = (int)request.Arguments[0];

          // Return the product itself
          return repository.GetById(id);
     }
}

// Do the same thing with the Client class
// (Note: I did a simple cut and paste to keep things simple--please forgive the duplication)
[Factory(typeof(Client))]
public class ClientFactory : IFactory
{
     object CreateInstance(IServiceRequest request)
     {
          // Grab a copy of the IRepository from the container
          var repository = container.GetService>();

          // Get the id (this assumes that your id is an Int32)
          var id = (int)request.Arguments[0];

          // Return the client itself
          return repository.GetById(id);
     }
}

[Factory(typeof(Transaction))]
public class TransactionFactory : IFactory
{
     object CreateInstance(IServiceRequest request)
     {
        // Note: Argument checking has been removed for brevity
        var container = request.Container;
        var arguments = request.Arguments;
        var productId = (int)arguments[0];
        var clientId = (int)arguments[1];

        // Get the product and the client
        var product = container.GetService(productId);
        var client = container.GetService(clientId);

        // Create the transaction itself
        return new Transaction(product, client);
     }
}

// Make this implementation a singleton
[Implements(typeof(MarginCalculator), LifecycleType.Singleton)]
public class ConcreteMarginCalculatorA : MarginCalculator
{
    public override double CalculateMargin()
    {
        // Perform actual calculation
    }
}

Once you have all that code compiled in one of your assemblies, here's all you need to do to load it into the container:

var container = new ServiceContainer();
container.LoadFrom(AppDomain.CurrentDomain.BaseDIrectory, "YourAssembly.dll");

...Now for the fun part. In order to create your transaction object with the given product and client ID, here's the call you need to make to LinFu.IOC's container:

int productId = 12345;
int clientId = 54321;
string serviceName = null;

// Not pseudocode :)
var transaction = container.GetService(serviceName, productId, clientId);

What makes this interesting is that despite the number of dependencies you might have, LinFu's IOC container will handle 90% of the boilerplate code for you so you don't have to do all this stuff on your own. The best part is that all the implementations above will all be determined/resolved at runtime.

You can practically swap implementations while the program is running, and you can even replace implementations without even recompiling your application. You can find more info here:

http://www.codeproject.com/KB/cs/LinFu_IOC.aspx

HTH :)