Griffin.Container is a somewhat performant Inversion of control container. It's goal is not to be the fastest container nor the one with the most feature rich API.
Instead I've tried to take a convenience over configuration approach. You'll probably have to design your applications better instead of using a lot of container features . For more information about best practices read my previous IoC / Dependency injection article.
The goal with this article is to show you the powers of Griffin.Container and it's hopefully easy API. A quick note on the samples zipfile: The samples are complimentary to the article. Both read the article and download the zip to get the most out of everything.
Let's start with an easy hello world project.
Create a new console project and run "install-package griffin.container
" from the package manager console. Then add the following code:
public class Hello
{
public void World()
{
Console.WriteLine("Hello world");
}
}
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterConcrete<Hello>(Lifetime.Transient);
var container = registrar.Build();
var hello = container.Resolve<Hello>();
hello.World();
Console.WriteLine("Press enter to quit");
Console.ReadLine();
}
}
That's it.
The container will create an object of the type Hello
when you invoke Resolve()
. You will get a new instance each time you call resolve. That's what Transient
means. Let's illustrate that:
public class Hello
{
private static int Counter;
private int _myId;
public Hello()
{
_myId = Counter++;
}
public void World()
{
Console.WriteLine("Hello world " + _myId);
}
}
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterConcrete<Hello>(Lifetime.Transient);
var container = registrar.Build();
container.Resolve<Hello>().World();
container.Resolve<Hello>().World();
container.Resolve<Hello>().World();
container.Resolve<Hello>().World();
Console.WriteLine("Press enter to quit");
Console.ReadLine();
}
}
That will display:
Hello world 0
Hello world 1
Hello world 2
Hello world 3
Since we invoked Resolve()
four times. If we want the same instance every time we can instead say that the "Hello" class is Singleton. That requires only one line of code change:
registrar.RegisterConcrete<Hello>(Lifetime.Singleton);
Now run the code again and your see that "Hello world 0" will be repeated four times.
Limited lifetime / Scoping / Cleanup
Another important aspect of the container is to be able to dispose classes when they have been used. Let's change the
Hello
class so that it implements
IDisposable
.
public class Hello : IDisposable
{
public void Dispose()
{
Console.WriteLine("I'm being disposed like trash :(");
}
}
The container uses something called scopes for this. A scope simply means that the container keeps track of all created objects that have a limited lifetime. The actual lifetime ends when you dispose the scope. A scope is used like this:
using (var scope = container.CreateChildContainer())
{
var hello = scope.Resolve<Hello>();
}
Nothing happens with the
Hello
objects since we have not marked the class as scoped. Let's do that:
registrar.RegisterConcrete<Hello>(Lifetime.Scoped);
That's all. Full example:
public class Hello : IDisposable
{
private static int Counter;
private int _myId;
public Hello()
{
_myId = Counter++;
}
public void World()
{
Console.WriteLine("Hello world " + _myId);
}
public void Dispose()
{
Console.WriteLine("I'm being disposed like trash :(");
}
}
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterConcrete<Hello>(Lifetime.Scoped);
var container = registrar.Build();
using (var scope = container.CreateChildContainer())
{
scope.Resolve<Hello>().World();
scope.Resolve<Hello>().World();
scope.Resolve<Hello>().World();
scope.Resolve<Hello>().World();
}
Console.WriteLine("Press enter to quit");
Console.ReadLine();
}
}
You'll see that four objects will be created and disposed for you. Scoping is very useful for request/reply style of applications (for instance web applications or web services) since everything will be created and cleaned up automatically for you. No more need to manage state manually.
A design decision that I made with Griffin.Container is to never allow you to create scoped objects from the main container like this:
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterConcrete<Hello>(Lifetime.Scoped);
var container = registrar.Build();
var hello = container.Resolve<Hello>();
}
}
That example will throw an exception since you have configured
Hello
to be scoped but creating it in the main container which will effectivly make it a singleton. That's a dormant bug. The
Hello
class could have used a TCP connection or a database connection. Those may not be open for ever, hence the class would fail for all subsequent calls when the connection is dropped (since the connection state is not managed because the class was not designed to be long lived).
Working with interfaces
When starting using an IoC container you'll probably want to start using interfaces more frequent too. With a container you'll never have to care about more than the class contract (which usually is an interface). That is, you'll never have to know how or when you should create and dispose an object. The container takes care of that for you.
Let's let
Hello
implement one more interface:
public interface IPrintState
{
void PrintState(TextWriter writer);
}
public class Hello : IDisposable, IPrintState
{
private static int Counter;
private int _myId;
public Hello()
{
_myId = Counter++;
}
public void World()
{
Console.WriteLine("Hello world " + _myId);
}
public void PrintState(TextWriter writer)
{
writer.WriteLine("Hello #" + _myId + " is alive and kicking!");
}
public void Dispose()
{
Console.WriteLine("I'm being disposed like trash :(");
}
}
public class SomeOther : IPrintState
{
public void PrintState(TextWriter writer)
{
writer.WriteLine("I do something else");
}
}
The great thing with keeping interfaces small and well defined is that they are easy to reuse and implement. The following snippet is all which is required to print the state from all classes that implements the interface:
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterConcrete<Hello>(Lifetime.Transient);
registrar.RegisterConcrete<SomeOther>(Lifetime.Transient);
var container = registrar.Build();
var sb = new StringBuilder();
var writer = new StringWriter(sb);
foreach (var stateWriter in container.ResolveAll<IPrintState>())
{
stateWriter.PrintState(writer);
}
Console.WriteLine(sb.ToString());
Console.WriteLine("Press enter to quit");
Console.ReadLine();
}
}
Do note that you do not have to do anyting special to register
Hello
as an implementation of
IPrintState
. The method
registrar.RegisterConcrete<hello>()</hello>
will register the class as all non .NET interfaces.
Dependency injection
The previous examples did only show you how you can create objects and control the object lifetimes. That's the inversion of control part. However, IoC containers are also useful for dependency injection. That is to identify and pass all dependencies to your classes.
This is usually done using constructor injection (which also is the only supported method in Griffin.Container). Constructor injection is useful since you directly can identify which dependencies a class has.
public class UserService
{
public UserService(IUserRepository repository)
{
}
}
That class tells us that the user service needs a data source to load/store changes that it makes to the users. The following snippet to the same thing:
public class UserService
{
public UserService()
{
_userRepository = new UserRepository("MyConnectionString");
}
}
The difference is that the first example is not tied to a specific implementation (data source). We can also control the lifetime of the repository independently of the user service instances. We can for instance let all UserService
instances share the same repository instance to be able to do caching inside it.
As for the registrations, you do not have to do anything special to take advantage of dependency injection. The container handles it automatically for you. Remember to prefer interfaces over classes as dependencies. And try to keep them as small as possible to make the code more flexible and easier to refactor.
Best practice
Depend on abstractions (interfaces) instead of concretes (classes). Your application get's easier to refactor. Try to divide your interfaces into smaller pieces. For instance the IUserRepository
could be IUserQueries
and IUserStorage
. They could however still be implemented by the same class (but the queries could be moved, cached etc later on without having to modify the dependent code).
Registrations
The registration process is where all containers differs. I recommend that you do not register every single class manually (by invoking one of the registrar.RegisterXXX()
methods). Sure. You should avoid change/refactor code (be SOLID). But in reality there are a very few of us which can write code so that no refactoring is required. Instead we do change our classes. Those changes will affect the registrations. The best way to keep them in sync is to manage the registrations (lifetime) from the classes themselves.
Best Practice
Use the [Component]
attribute where it's possible. It's both visible in sandcastle generated documentation and makes it easier to refactor the application. Try to use the same lifetime for all classes if possible (i.e. do not specify it in the attribute but in the LoadComponents()
line).
[Component] attribute
To make the
Hello
class a scoped you can just write:
[Component(Lifetime.Scoped)]
public class HelloWorld
{
}
And register it in the registrar with:
registrar.RegisterComponents(Lifetime.Scoped, Assembly.GetExecutingAssembly());
Notice three things:
RegisterComponents
will register all classes in an assembly thas has been decorated with the [Component]
attribute.- We specify a lifetime in the
[Component]
attribute. That explicitly tells us that the class must have a specific lifetime. - We specify an additional lifetime in the
RegisterComponents()
attribute that will be used for all classes which have not a lifetime specified in [Component]
.
Example:
[Component(Lifetime = Lifetime.Scoped)]
public class Hello
{
private static int Counter;
private int _myId;
public Hello()
{
_myId = Counter++;
}
public void World()
{
Console.WriteLine("Hello world " + _myId);
}
}
[Component]
public class MyTransient
{
private static int Counter;
private int _myId;
public MyTransient()
{
_myId = Counter++;
}
public void Print()
{
Console.WriteLine("Transient Id " + _myId);
}
}
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterComponents(Lifetime.Transient, Assembly.GetExecutingAssembly());
var container = registrar.Build();
using (var scope = container.CreateChildContainer())
{
scope.Resolve<Hello>().World();
scope.Resolve<Hello>().World();
scope.Resolve<Hello>().World();
scope.Resolve<MyTransient>().Print();
scope.Resolve<MyTransient>().Print();
scope.Resolve<MyTransient>().Print();
}
Console.WriteLine("Press enter to quit");
Console.ReadLine();
}
}
The same registration line (the call to
RegisterComponents()
) should work for most of your own classes.
Custom registrations
Sometimes you need more finegraned control over the registration process. A nhibernate/EF4/RavenDB connection is a typical example. A registration for RavenDb would look like this:
registrar.RegisterService<IDocumentSession>(container => _documentStore.OpenSession(), Lifetime.Scoped);
The registration interface in Griffin.Container looks like this:
public interface IContainerRegistrar
{
IEnumerable<ComponentRegistration> Registrations { get; }
void RegisterComponents(Lifetime defaultLifetime, string path, string filePattern);
void RegisterComponents(Lifetime defaultLifetime, params Assembly[] assemblies);
void RegisterModules(string path, string filePattern);
void RegisterModules(params Assembly[] assemblies);
void RegisterConcrete<TConcrete>(Lifetime lifetime) where TConcrete : class;
void RegisterConcrete(Type concrete, Lifetime lifetime);
void RegisterService<TService>(Func<IServiceLocator, TService> factory, Lifetime lifetime);
void RegisterService(Type service, Func<IServiceLocator, object> factory, Lifetime lifetime);
void RegisterType<TService, TConcrete>(Lifetime lifetime) where TService : class where TConcrete : TService;
void RegisterType(Type service, Type concrete, Lifetime lifetime);
void RegisterInstance<TService>(TService instance) where TService : class;
void RegisterInstance(Type service, object concrete);
}
A link to the online documentation can be found at the bottom of this article.
RegisterComponents
Used to register all classes which has been tagged with the
[Component]
attribute. It can either scan one/more specified assemblies or load/scan assemblies in a specific folder.
RegisterModules
Modules are a great way to move the registration process from one single location into several and by doing so allow each module in your system to be in charge of it's own registrations. That makes your application easier to mantain and it gives you an better overview of what each module registers.
I'll come back to modules later.
RegisterConcrete
RegisterConcrete
will register the class as itself in the container:
public class MyClass
{
}
registrar.RegisterConcrete<MyClass>();
container.Resolve<MyClass>();
or as implemented interfaces (if there are any and that they are not .NET framework interfaces).
public class MyClass : IEnumerable<Item>, IMyService
{
}
registrar.RegisterConcrete<MyClass>();
container.Resolve<IMyService>();
contianer.Resolve<IEnumerable<Item>>();
RegisterService
Register an service (typically an interface) using a custom factory method. Typically only used to register external dependencies (such as db libraries) in the container.
RegisterType
Register a specific service (interface) to a specific concrete (class). Typically only used to register external dependencies (such as db libraries) in the container.
RegisterInstance
Register a singleton which has already been created somewhere.
Modules
Griffin.Container allows you to use modules to move the registration responsibility from a single location to each module/package in your application. All you need is to create a new class and let it inherit IContainerModule
.
public class UserRegistrations : IContainerModule
{
public void Register(IContainerRegistrar registrar)
{
registrar.RegisterConcrete<UserService>(Lifetime.Scoped);
registrar.RegisterConcrete<UserCache>(Lifetime.Singleton);
registrar.RegisterConcrete<UserRepository>(Lifetime.Scoped);
}
}
Next you have to load all modules:
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterModules(Environment.CurrentDirectory, "MyApp.*.dll");
var container = registrar.Build();
}
}
Plugin architectures becomes obsolete (unless you have to be able to disable/unload plugins) with this technique. Simply drop a new assembly in the app folder and restart the application.
Best practice
Use a composition root in every project, use it to call registrar.RegisterComponents()
for the current assembly (i.e put a ContainerModule
in the root namespace of the project).
Domain events
Griffin.Container has built in support for domain events (handled within the same process). Domain events means that you send an event when something have happened. For instance UserCreated
, AccountLocked
, ReplyPosted
etc. Those events enables you to create loosely coupled applications. You could for instance subscribe on the event ReplyPosted
to be able to send out email notifcations (without having to change the class(es) that created/saved the reply).
The difference between the event model in .NET and the event model in Griffin.Container is that the latter is loosely coupled.
Subscribing
Subscribing is easy. Simply let any class implement
IHandlerOf<T>
:
[Component]
public class ReplyEmailNotification : IHandlerOf<ReplyPosted>
{
ISmtpClient _client;
IUserQueries _userQueries;
public ReplyEmailNotification(ISmtpClient client, IUserQueries userQueries)
{
_client = client;
_userQueries = userQueries;
}
public void Invoke(ReplyPosted e)
{
var user = _userQueries.Get(e.PosterId);
_client.Send(new MailMessage(user.Email, "bla bla"));
}
}
Dispatching
Domain events are dispatched using the
DomainEvent
class. The actual domain event can be any class, there are no restrictions. I do however recommend that you treat them as DTO's.
public class UserCreated
{
public UserCreated(string id, string displayName)
{
}
}
public class UserService
{
public void Create(string displayName)
{
DomainEvent.Publish(new UserCreated(user.Id, user.DisplayName));
}
}
Best practice
Avoid attaching domain objects/models to the events, but try to treat the domain events as immutable DTO's instead. It makes it a lot easier if you have to scale your application later on. You can always load the correct domain model through the repositories/queries when you receive the domain event.
Commands
Griffin.Container do also have a built in command pattern implementation. I've chosen to seperate the actual handling (ICommandHandler<T>
) from the command class (ICommand
). That enables us to handle the command anywhere. You can for instance start by handling all commands in the same server/process but later on route some commands to a different server.
Always try to create one command per use case instead of creating small commands which you chain. Chained commands will most likely lead to a mess when the application grows.
As I mention before we can scale the application by moving the command handling to different processes. To enable that we have to treat all commands as asynchronous. That means two things:
- A command can never return something.
- Don't expect the command to have been executed just because
ICommandDispatcher.Dispatch()
has returned.
Instead wait for the domain events and use those for the extra processing.
Setup
The command dispatcher do not have a default implementation. Instead we have to assign one. There is a built in one which is called
ContainerDispatcher
. As you might have guessed that it uses the IoC container to find the correct handler.
CommandDispatcher.Assign(new ContainerDispatcher(myContainer));
However, since commands typically are executed from services, controllers etc I do recommend that you register the ICommandDispatcher
implementation in the container and get it through dependency injection. (although the dispatcher could be considered to be an infrastructure component, up for discussion).
A command
A command is a regular .NET class which is used to transfer the command and it's arguments to a command handler.
Example command:
public class CreateUser
{
public CreateUser(string userName)
{
if (userName == null) throw new ArgumentNullException("userName");
UserName = userName;
}
public string UserName { get; private set; }
public string DisplayName { get; set; }
}
Notice that the
userName
is passed in the constructor (and has a private setter) while
DisplayName
can be set through the property. That indicates that the
UserName
is mandatory while the
DisplayName
is optional. Always try to follow this pattern since it makes easier to tell which information is required.
Handling a command
A command is handled by classes that implement the
IHandlerOf<T>
interface:
public class CreateUserHandler : IHandlerOf<CreateUser>
{
IUserRepository _repos;
public CreateUserHandler(IUserRepository userRepository)
{
_repos = userRepository;
}
public void Invoke(CreateUser cmd)
{
var user = _repos.Create(cmd.UserName, cmd.DisplayName);
DomainEvent.Dispatch(new UserCreated(user));
}
}
In this example I used a repository, but it could have been vanilla ADO.NET or a webservice. It really doesn't matter since no code is dependent on the implementation.
Invoking a command
Simple:
CommandDispatcher.Dispatch(new CreateUser("arne"));
I do recommend that you register the dispatcher in the container and take it as a dependency in your own classes. It makes the intent more clear (even though it can be considered to be a infrastructure dependency).
public class SampleService
{
ICommandDispatcher _dispatcher;
public SampleService(ICommandDispatcher dispatcher)
{
if (dispatcher == null) throw new ArgumentNullException("dispatcher");
_dispatcher = dispatcher;
}
public void ProcessStuff()
{
_dispatcher.Invoke(new StoreResult(someResult));
}
}
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterConcrete<ContainerDispatcher>(Lifetime.Singleton);
registrar.RegisterConcrete<SampleService>(Lifetime.Transient);
var container = registrar.Build();
}
}
Decorators
The
decorator pattern allows us to "decorate" a class to give it more functionality. Griffin.Container let's you create decorators by implementing the interface
IInstanceDecorator
:
public interface IInstanceDecorator
{
void PreScan(IEnumerable<Type> concretes);
void Decorate(DecoratorContext context);
}
It can be a bit tedious to do it on your own. You can therefore use the Griffin.Container.Interception
nuget package for the container to make it a bit easier.
Let's log all method calls for every single class that we have registered in the container. Might sound hard, but it's quite simple:
Start by running install-package griffin.container.interception
from the package manager console. Then enter the following code:
public class ConsoleLoggingDecorator : CastleDecorator
{
public override void PreScan(IEnumerable<Type> concretes)
{
}
protected override IInterceptor CreateInterceptor(DecoratorContext context)
{
return new LoggingInterceptor();
}
}
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var args = string.Join(", ", invocation.Arguments.Select(x => x.ToString()));
Console.WriteLine("{0}({1})", invocation.Method.Name, args);
invocation.Proceed();
}
}
Do note that all intercepted classes must have virtual methods for it to work (limitation by the Castle.Proxy).
Register the decorator in your container:
container.AddDecorator(new ConsoleLoggingDecorator());
Let's try it with our repository:
container.Resolve<IUserRepository>().Get(10);
The console prints:
Get(10)
Be aware of that decorators hurt performance. But not as much as you would think (google performance benchmarks castle proxy).
Built in decorators
There are one decorator built into the Interception package.
Exception logger
This decorator will intercept all classes and log all exceptions together with the method name and all argument values.
Sample output:
SampleApplication.Users.UserService.Get(10) threw an exception:
(exception info would be here if this were not an article, which it is)
The exception will of course still be thrown. It's up to you to handle it.
(The decorator where just added to illustrate how you can create your own)
Decorator summary
Combining command handlers and decorators can be really useful. You can for instance validate all commands using DataAnnotations with a decorator before the real handler is invoked. That's validation in one decorator instead of in every command handler. You could also create a command handler decorator which takes care of the database transaction.
Extending the container
I've avoided extension methods like the pest in the core since extension methods prevent extension through inheritance (which you all know is one of the foundations of OOP). The core itself has been divided into three interfaces.
Feel free to check the source code when reading this section, since it will make a lot more sense then.
The first on is the IContainerRegistrar
. It's purpose is to expose the registration interface used when register services and concretes in the container. That's it's only responsibility. Everything registered is exposed by the Registrations
property.
The next interface is the IContainerBuilder
. It's purpose is to analyze all registrations and create build plans (IBuildPlan
) for each service. The build plan is used to create/access all classes that implements a service. To do that it stores an IInstanceStrategy
for each concrete/class. The stategy decides when it's time to create a new instance and when it should fetch it from the storage.
The final part is the service location. The core interface is IServiceLocator
which contains the basic resolution methods. It's inherited by IParentContainer
which adds the CreateChildContainer()
method and by the IChildContainer
interface which also inherits IDisposable
.
Framework libraries
There are also some framework libraries which you can use for your favorite framework.
Feel free to contribute with more packages.
Summary
I hope that you have enjoyed this article and that you will try out the container.
Do not forget to download the sample code since it takes you step-by-step through the features of griffin.container.
The code is also available at github: