How Do the Major C# Di/Ioc Frameworks Compare

How do the major C# DI/IoC frameworks compare?

While a comprehensive answer to this question takes up hundreds of pages of my book, here's a quick comparison chart that I'm still working on:

A table explaining difference between several DICs

Comparison (pros/cons) of various .NET dependency injection frameworks

Here is a nice performance benchmark:

  • IoC Container Benchmark - Performance comparison

Which .NET Dependency Injection frameworks are worth looking into?

edit (not by the author): There is a comprehensive list of IoC frameworks available at https://github.com/quozd/awesome-dotnet/blob/master/README.md#ioc:

  • Castle Windsor - Castle Windsor is best of breed, mature Inversion of Control container available for .NET and Silverlight
  • Unity - Lightweight extensible dependency injection container with support for constructor, property, and method call injection
  • Autofac - An addictive .NET IoC container
  • DryIoc - Simple, fast all fully featured IoC container.
  • Ninject - The ninja of .NET dependency injectors
  • Spring.Net - Spring.NET is an open source application framework that makes building enterprise .NET applications easier
  • Lamar - A fast IoC container heavily optimized for usage within ASP.NET Core and other .NET server side applications.
  • LightInject - A ultra lightweight IoC container
  • Simple Injector - Simple Injector is an easy-to-use Dependency Injection (DI) library for .NET 4+ that supports Silverlight 4+, Windows Phone 8, Windows 8 including Universal apps and Mono.
  • Microsoft.Extensions.DependencyInjection - The default IoC container for ASP.NET Core applications.
  • Scrutor - Assembly scanning extensions for Microsoft.Extensions.DependencyInjection.
  • VS MEF - Managed Extensibility Framework (MEF) implementation used by Visual Studio.
  • TinyIoC - An easy to use, hassle free, Inversion of Control Container for small projects, libraries and beginners alike.
  • Stashbox - A lightweight, fast and portable dependency injection framework for .NET based solutions.

Original answer follows.


I suppose I might be being a bit picky here but it's important to note that DI (Dependency Injection) is a programming pattern and is facilitated by, but does not require, an IoC (Inversion of Control) framework. IoC frameworks just make DI much easier and they provide a host of other benefits over and above DI.

That being said, I'm sure that's what you were asking. About IoC Frameworks; I used to use Spring.Net and CastleWindsor a lot, but the real pain in the behind was all that pesky XML config you had to write! They're pretty much all moving this way now, so I have been using StructureMap for the last year or so, and since it has moved to a fluent config using strongly typed generics and a registry, my pain barrier in using IoC has dropped to below zero! I get an absolute kick out of knowing now that my IoC config is checked at compile-time (for the most part) and I have had nothing but joy with StructureMap and its speed. I won't say that the others were slow at runtime, but they were more difficult for me to setup and frustration often won the day.

Update

I've been using Ninject on my latest project and it has been an absolute pleasure to use. Words fail me a bit here, but (as we say in the UK) this framework is 'the Dogs'. I would highly recommend it for any green fields projects where you want to be up and running quickly. I got all I needed from a fantastic set of Ninject screencasts by Justin Etheredge. I can't see that retro-fitting Ninject into existing code being a problem at all, but then the same could be said of StructureMap in my experience. It'll be a tough choice going forward between those two, but I'd rather have competition than stagnation and there's a decent amount of healthy competition out there.

Other IoC screencasts can also be found here on Dimecasts.

in which cases should be used certain types of IoC frameworks

Each and every one has a fatal flaw.

On a more serious note they surely do essentially the same thing, but differ in implementation details, conventions, performance, auxiliary features and suggested usecases.

I don't think that you should really sweat picking the IoC container. Stick to one you're used to and continue with your core functionality.

IoC comparisions

It is not in your list but consider looking at Castle Windsor

Number of facilities such as supporting Factories so that the factory product is injected not the factory itself or the Wcf facility for injecting a proxy or creating a host with dependencies.

Has a nice fluent interface for wiring up individual items but preferably you would use a convention to auto wire up so that you don't have to list each component.

Some other nice features:

  • Requires a single dll since new revision.
  • Supports injecting lists and arrays via SubDependancy resolvers

Here is a poll taken this year showing the favoured IoC. Unity seems to be top but that is probably because it is a Microsoft product and Microsoft workshops will pick it up by default.

How to choose a DI container?

This is like buying a car. You might like a Toyota, but it's just 2.5L engine. You might like Ferrari, but it's too red. You might like Mazda, but your boss doesn't allow you to drive it. You might like Hummer, but then your colleagues would laugh at you. Mix the manufacturers to your taste, there's always going to be something missing for somebody or at some different moment.

My take is - first and foremost, DI is usually better then not having DI. Pick anything and you'll be better off. I'd pick something that:

  • Has good support in community (so you can get answers)
  • Has a good backing company behind it (so you don't get to rewrite your code when it goes bust)
  • Feels good to you (so you don't swear in front of the kids, not cool)
  • Is not an overkill for the project
  • Is not just DI, but offers an ecosystem of things that will reduce the time you spend on tasks that you know you can do, just not right now - and then you can focus on things that matter
  • Is used by a lot of people (so you know that many parts are also tested in real life and bugs filled)
  • Isn't 5 years old (such as that documentation says it is supported on Windows 98 or something)

My 2 cents - http://www.springframework.net/. I mean, their documentation contents page is like 20 pages long...

Or you just might want to look at some more answers to a similar question:

  • Which .NET Dependency Injection frameworks are worth looking into?

Hiro vs other IoC containers

Hiro claims to be the fastest container. This statement is based on the benchmark given by the author (see here for an objective comparison between many containers). Whether or not this benchmark is realistic depends upon the size of your application. The benchmark seems to be cleverly setup with a very small set of registered types. When adding more registrations to the benchmark, the performance starts dropping (see below for an example benchmark of this). When looking closely we can see that Hiro has a performance characteristic of O(n), while normal DI frameworks have a characteristic of O(1), thus with other frameworks the performance stays constant with the number of registered types.

What’s nice about Hiro is that it generates a new assembly on the fly and resolving a new type consists of just a single interface call. This is very fast. Most common DI frameworks on the other hand, will always have to do a dictionary lookup during a call to GetInstance. Hiro’s GetInstance method is basically a big switch case statement internally, implemented as a bunch of if-statements. If-based switch case statements are extremely fast up until a point. The C# compiler uses an heuristic of (I believe) 18 case statements as turning point. Below that number the compiler generates a bunch of if-statements. Above that number it creates and stores a static dictionary and does a dictionary lookup, because above 18 lookups performance of a dictionary lookup if simply faster.

Well guess what; Hiro doesn’t use the optimization as the C# compiler does. That's why the performance keeps dropping as more and more types are added to the container. A linear performance characteristic –or O(n) for short- is okay for small sets of data, but any well written and commonly sized, dependency-friendly application has many type registrations / mappings. And in that case the performance of Hiro drops quickly.

So, to answer your questions:

Is it still the fastest IOC container
today?

For very small applications it is the fastest. For any normal sized applications it has never been the fastest.

Is it ready for production?

Hard to say. Its current status is alpha and it misses a lot of features that other IOC framework have. The developer just (see comments below) published a stable release. This means that according to the developer it is ready for production.

What are its major advantages
and disadvantages over other IOC containers?

Take a look for instance at this article (and the follow up) which gives a good (feature) comparison of IOC frameworks. The writer of the article has a vision of what he thinks is important. You have to make such a list for yourself. Performance seems to be high on your list. However, please note that there are other ways to improve performance. For instance by registering types as singletons to prevent the creation of many types.

Here is a little comparison with other containers. Note that I didn't write Hiro, so I could be missing things, especially since there seems to be no documentation at all, but here goes:

  1. You need 4 assemblies to run, instead of 1 or 2 for most IOC frameworks.
  2. It throws a stack overflow when a recursive dependency is resolved (most frameworks do this btw). So there is no cyclic dependency detection.
  3. Doesn't seem to support any lifestyle other than transient (the writer does say it will, but I currently see no support for this). This actually is bad for performance, because most services would normally be registered as singletons. According to the author, it supports multiple lifestyles.
  4. Doesn't support resolving open generic types.
  5. Doesn't support unregistered type resolution.
  6. I has no documentation accept XML (intellisense) documentation.

Are there any other containers can do
IOC at compile time?

Define 'compile time'. Hiro generates a new assembly on the fly once during runtime. Others (like Autofac, Windsor and Simple Injector) emit IL or compile delegates under the covers. This isn't any slower than compiling the complete assembly in one go (however, there is the overhead of calling the delegate which is a tiny bit slower than making an interface call).

BTW, I changed the benchmark to see the behavior described above appear. With 50 extra types registered you will see that Hiro performs already three times as slow as with the initial benchmark. Here is the code I used:

public interface IHandler<T> { }

public class Handler<T> : IHandler<T> { }

public class HiroUseCase : UseCase
{
IMicroContainer container;

private static void RegisterHandler<T>(DependencyMap map)
{
map.AddService(typeof(IHandler<T>), typeof(Handler<T>));
}

public HiroUseCase()
{
var map = new DependencyMap();

// *** My added registrations
RegisterHandler<byte>(map);
RegisterHandler<byte?>(map);
RegisterHandler<short>(map);
RegisterHandler<short?>(map);
RegisterHandler<ushort>(map);
RegisterHandler<ushort?>(map);
RegisterHandler<int>(map);
RegisterHandler<int?>(map);
RegisterHandler<uint>(map);
RegisterHandler<uint?>(map);

RegisterHandler<long>(map);
RegisterHandler<long?>(map);
RegisterHandler<ulong>(map);
RegisterHandler<ulong?>(map);
RegisterHandler<float>(map);
RegisterHandler<float?>(map);
RegisterHandler<double>(map);
RegisterHandler<double?>(map);
RegisterHandler<decimal>(map);
RegisterHandler<decimal?>(map);

RegisterHandler<DateTime>(map);
RegisterHandler<DateTime?>(map);
RegisterHandler<char>(map);
RegisterHandler<char?>(map);
RegisterHandler<object>(map);
RegisterHandler<string>(map);
RegisterHandler<bool>(map);
RegisterHandler<bool?>(map);
RegisterHandler<Enum>(map);
RegisterHandler<DateTimeKind>(map);

RegisterHandler<DateTimeKind?>(map);
RegisterHandler<DateTimeOffset>(map);
RegisterHandler<DateTimeOffset?>(map);
RegisterHandler<DayOfWeek>(map);
RegisterHandler<DayOfWeek?>(map);
RegisterHandler<DBNull>(map);
RegisterHandler<Delegate>(map);
RegisterHandler<DivideByZeroException>(map);
RegisterHandler<DllNotFoundException>(map);
RegisterHandler<Exception>(map);

RegisterHandler<KeyNotFoundException>(map);
RegisterHandler<InvalidOperationException>(map);
RegisterHandler<InvalidCastException>(map);
RegisterHandler<InvalidProgramException>(map);
RegisterHandler<InvalidTimeZoneException>(map);
RegisterHandler<IDisposable>(map);
RegisterHandler<IComparable>(map);
RegisterHandler<IEquatable<int>>(map);
RegisterHandler<IEnumerable>(map);
RegisterHandler<IEqualityComparer>(map);

// *** Original benchmark setup
map.AddService(typeof(IWebApp), typeof(WebApp));
map.AddService(typeof(IAuthenticator), typeof(Authenticator));
map.AddService(typeof(IStockQuote), typeof(StockQuote));
map.AddService(typeof(IDatabase), typeof(Database));
map.AddService(typeof(IErrorHandler), typeof(ErrorHandler));
map.AddService(typeof(ILogger), typeof(Logger));

IContainerCompiler compiler = new ContainerCompiler();
var assembly = compiler.Compile(map);;

var loadedAssembly = assembly.ToAssembly();
var containerType = loadedAssembly.GetTypes()[0];
container = (IMicroContainer)Activator
.CreateInstance(containerType);
}

public override void Run()
{
var webApp =
(IWebApp)container.GetInstance(typeof(IWebApp), null);
webApp.Run();
}
}


Related Topics



Leave a reply



Submit