Scrivo sempre un wrapper adattatore per qualsiasi contenitore IoC, che assomiglia a questo:
public static class Ioc
{
public static IIocContainer Container { get; set; }
}
public interface IIocContainer
{
object Get(Type type);
T Get<T>();
T Get<T>(string name, string value);
void Inject(object item);
T TryGet<T>();
}
Per Ninject, in particolare, la classe dell'adattatore concreto si presenta così:
public class NinjectIocContainer : IIocContainer
{
public readonly IKernel Kernel;
public NinjectIocContainer(params INinjectModule[] modules)
{
Kernel = new StandardKernel(modules);
new AutoWirePropertyHeuristic(Kernel);
}
private NinjectIocContainer()
{
Kernel = new StandardKernel();
Kernel.Load(AppDomain.CurrentDomain.GetAssemblies());
new AutoWirePropertyHeuristic(Kernel);
}
public object Get(Type type)
{
try
{
return Kernel.Get(type);
}
catch (ActivationException exception)
{
throw new TypeNotResolvedException(exception);
}
}
public T TryGet<T>()
{
return Kernel.TryGet<T>();
}
public T Get<T>()
{
try
{
return Kernel.Get<T>();
}
catch (ActivationException exception)
{
throw new TypeNotResolvedException(exception);
}
}
public T Get<T>(string name, string value)
{
var result = Kernel.TryGet<T>(metadata => metadata.Has(name) &&
(string.Equals(metadata.Get<string>(name), value,
StringComparison.InvariantCultureIgnoreCase)));
if (Equals(result, default(T))) throw new TypeNotResolvedException(null);
return result;
}
public void Inject(object item)
{
Kernel.Inject(item);
}
}
La ragione principale per fare ciò è di astrarre il framework IoC, così posso sostituirlo in qualsiasi momento - dato che la differenza tra i framework è generalmente nella configurazione piuttosto che nell'utilizzo.
Ma, come bonus, le cose diventano anche molto più semplici per usare il framework IoC all'interno di altri framework che non lo supportano intrinsecamente. Ad esempio, per WinForms sono due passaggi:
Nel tuo metodo Main, crea semplicemente un'istanza di un contenitore prima di fare qualsiasi altra cosa.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
try
{
Ioc.Container = new NinjectIocContainer( /* include modules here */ );
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyStartupForm());
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
E poi avere una Forma base, da cui derivano altre forme, che chiama Inject su se stessa.
public IocForm : Form
{
public IocForm() : base()
{
Ioc.Container.Inject(this);
}
}
Questo dice all'euristica del cablaggio automatico di tentare di iniettare ricorsivamente tutte le proprietà nel modulo che si adattano alle regole impostate nei moduli.