Migrating from Simple Injector to Caste Windsor
Castle Windsor is part of the Castle Project. It is one of the oldest and most used DI frameworks for .NET. It offers a very rich and extensible API. Advanced scenario’s however, are often hard to implement unless you have a very good understanding
of the complete API.
Core differences with Simple Injector
- The default lifestyle is Singleton.
- Every resolved instance has to be releases explicitly to prevent memory leaks.
- Tends to swallow errors, which makes it hard to find configuration bugs.
- By default, concrete instances can not be resolved without registration.
- Scans new instances for writeable properties and assigns a value if it can provide a component that matches the type of the property (implicit property
injection).
- Does not allow resolving collections of objects through constructor injection by default. Add the
ArrayResolver to allow resolving arrays. Add the
EnumerableResolver to allow resolving IEnumerable<T> collections.
Code Samples
The following section contains code snippets that show how to resolve and register instances with the Simple Injector on the left-hand side, and equivalent snippets for Castle Windsor on the right-hand side.
Resolving Instances
| Simple Injector |
Castle Windsor |
| container.GetInstance<IService>(); |
container.Resolve<IService>(); |
| container.GetInstane(typeof(IService)); |
container.Resolve(typeof(IService)); |
| IEnumerable<IFilter> filters = container.GetAllInstances<IFilter>(); |
IFilter[] filters = container.ResolveAll<IFilter>(); |
IEnumerable<object>
filters =
container.GetAllInstances(typeof(IFilter)); |
IFilter[] filters =
container.ResolveAll(typeof(IFilter)); |
Registration
Registration with a transient lifetime.
| Simple Injector |
Castle Windsor |
// Optional. Concrete instances can be
// resolved without registration
container.Register<RealService>(); |
// Required. Concrete instances will not
// be resolved without registration.
container.Register(Component
.For<RealService>().LifeStyle.Transient); |
| container.Register<IService,
RealService>(); |
container.Register(Component.For<IService>()
.ImplementedBy<RealService()
.LifeStyle.Transient); |
container.Register<IService>(() =>
{
var log = container.GetInstance<ILogger>();
return new RealService(logger);
}); |
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IService>()
.UsingFactoryMethod(() =>
{
var
log = container.Resolve<ILogger>();
return new RealService(log);
})
.LifeStyle.Transient); |
Registration with a singleton lifetime.
| Simple Injector |
Castle Windsor |
| container.RegisterSingle<RealService>(); |
container.Register(Component.For<RealService>()); |
container
.RegisterSingle<IService,
RealService>(); |
container.Register(Component.For<IService>().ImplementedBy<RealService>()); |
container.RegisterSingle<IService>(() =>
{
var log = container.GetInstance<ILogger>();
return new RealService(log);
}); |
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IService>()
.UsingFactoryMethod(() =>
{
var
log = container.Resolve<ILogger>();
return new RealService(log);
})); |
container.RegisterSingle<IService>(
new
RealService(new
ConsoleLogger())); |
container.Register(Component.For<IService>()
.Instance(
new RealService(new
ConsoleLogger()))); |
container.RegisterAll<IFilter>(
new
XssInjectionFilter(),
new
SqlInjectionFilter(),
new
SmartFilter()
); |
container.Register(Component.For<IFilter>()
.Instance(new
XssInjectionFilter()));
container.Register(Component.For<IFilter>()
.Instance(new
SqlInjectionFilter()));
container.Register(Component.For<IFilter>()
.Instance(new
SmartFilter())); |
IEnumerable<IFilter>
filters =
FilterProvider.GetFilters();
container.RegisterAll<IFilter>(filters); |
[not clear how to achieve this with Windsor] |
container.RegisterInitializer<ICommand>(cmd =>
{
cmd.Logger =
container.GetInstance<ILogger>();
}); |
// Castle Windsor will implicitly inject all
// writable properties for you. There’s nothing
// you need to do here. |
container.RegisterInitializer<ICommand>(cmd =>
{
cmd.SendAsync = true;
}); |
// The closest thing with Windsor is this:
container.Register(AllTypes.From(
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetExportedTypes()))
.BasedOn<ICommand>()
.WithService.Self()
.Configure(c => c.DependsOn(Property
.ForKey("SendAsync").Eq(true))
.LifeStyle.Transient));
//
Note that this is still not equivalent to the
// left side, since this Windsor registration
// is a batch registration, while the Simple
// Injector’s RegisterInitializer is just a hook
// for any type that implements ICommand. It does
// not register any type itself. Also note that
// the Windsor registration is not type-safe,
// since the name of the property is a string.
// This will break when the property name is
// refactored. |
Advanced Scenario’s
The SimpleInjector.Extensions.dll contains extension methods for the
Container class that enable many advanced scenario’s. The section below how the registrations done with the
Extensions library maps to those of Castle Windsor.
Registration using the non-generic API.
| Simple Injector |
Castle Windsor |
container.Register(typeof(IService),
typeof(RealService)); |
container.Register(Component
.For(typeof(IService))
.ImplementedBy(typeof(RealService))
.LifeStyle.Transient); |
container.RegisterAll<IFilter>(
from asm
in AppDomain.CurrentDomain
.GetAssemblies()
from type
in asm.GetExportedTypes()
where !type.IsAbstract
where
typeof(IFilter).IsAssignableFrom(type)
select type); |
container.Register(AllTypes.From(
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetExportedTypes()))
.BasedOn<IFilter>()
.WithService
.Select(new[] {
typeof(IFilter)
})
.Configure(c => c.LifeStyle.Transient)); |
Batch registration of generic interfaces
| Simple Injector |
Castle Windsor |
container.RegisterManyForOpenGeneric(
typeof(IHandler<>),
typeof(IHandler<>).Assembly); |
container.Register(AllTypes
.FromAssemblyContaining(typeof(IHandler<>))
.BasedOn(typeof (IHandler<>))
.Unless(t => t.IsGenericTypeDefinition)
.WithService.Select((_, baseTypes) =>
{
return
from t
in baseTypes
where t.IsGenericType
let td = t.GetGenericTypeDefinition()
where td ==
typeof(IHandler<>)
select t;
})
.Configure(c => c.LifeStyle.Transient)); |
container.RegisterManyForOpenGeneric(
typeof(IHandler<>),
AppDomain.CurrentDomain.GetAssemblies()); |
container.Register(AllTypes.From(
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetExportedTypes()))
.BasedOn(typeof(IHandler<>))
.Unless(t => t.IsGenericTypeDefinition)
.WithService.Select((_, baseTypes) =>
{
return
from
t in
baseTypes
where
t.IsGenericType
let td = t.GetGenericTypeDefinition()
where
td == typeof(IHandler<>)
select
t;
})
.Configure(c => c.LifeStyle.Transient)); |
var asm =
typeof(IHandler<>).Assembly;
container.RegisterManyForOpenGeneric(
typeof(IHandler<>),
from type
in asm.GetTypes()
where !type.IsAbstract
where type.Name.EndsWith("Handler")
select type); |
container.Register(AllTypes.From(
from
type in
asm.GetTypes()
where
!type.IsAbstract
where
type.Name.EndsWith("Handler")
select
type)
.BasedOn(typeof(IHandler<>))
.WithService.Select((_, baseTypes) =>
{
return
from
t in
baseTypes
where
t.IsGenericType
let dt = t.GetGenericTypeDefinition()
where dt ==
typeof(IHandler<>)
select
t;
})
.Configure(c => c.LifeStyle.Transient)); |
Mapping of open generic types to an open generic implementation
| Simple Injector |
Castle Windsor |
container.RegisterOpenGeneric(
typeof(IValidator<>),
typeof(NullValidator<>)); |
container.Register(Component
.For(typeof(IValidator<>))
.ImplementedBy(typeof(NullValidator<>))); |