Scrivo sempre un wrapper Adapter 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 Adapter concreta è simile alla seguente:
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);
}
}
Il motivo principale per farlo è quello di astrarre il framework IoC, quindi posso sostituirlo in qualsiasi momento, dato che la differenza tra i framework è generalmente nella configurazione piuttosto che nell'uso.
Ma, come bonus, le cose diventano anche molto più facili per l'utilizzo del framework IoC all'interno di altri framework che non lo supportano intrinsecamente. Per WinForms, ad esempio, sono due passaggi:
Nel tuo metodo principale, è sufficiente creare 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 quindi 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.