¿Cómo se comparan los principales marcos C # DI / IoC? [cerrado]

308

A riesgo de entrar en territorio de guerra santa, ¿cuáles son las fortalezas y debilidades de estos marcos populares de DI / IoC, y podría uno ser fácilmente considerado el mejor? ..:

  • Ninject
  • Unidad
  • Castillo Windsor
  • Autofac
  • EstructuraMapa

¿Hay otros marcos DI / IoC para C # que no haya enumerado aquí?

En el contexto de mi caso de uso, estoy creando una aplicación WPF de cliente y una infraestructura de servicios WCF / SQL, facilidad de uso (especialmente en términos de sintaxis clara y concisa), documentación consistente, buen soporte comunitario y rendimiento son factores importantes. En mi elección.

Actualizar:

Los recursos y las preguntas duplicadas citadas parecen estar desactualizadas. ¿Puede alguien con conocimiento de todos estos marcos presentarse y proporcionar una idea real?

Me doy cuenta de que es probable que la mayoría de las opiniones sobre este tema sea parcial, pero espero que alguien se haya tomado el tiempo de estudiar todos estos marcos y tener al menos una comparación generalmente objetiva.

Estoy bastante dispuesto a hacer mis propias investigaciones si esto no se ha hecho antes, pero asumí que esto era algo que al menos algunas personas ya habían hecho.

Segunda actualización

Si tiene experiencia con más de un contenedor DI / IoC, clasifique y resuma los pros y los contras de ellos, gracias. Este no es un ejercicio para descubrir todos los pequeños contenedores oscuros que la gente ha hecho, estoy buscando comparaciones entre los marcos populares (y activos).

ocodo
fuente
1
La misma pregunta que [Ninject vs Unity for DI] ( stackoverflow.com/questions/1054801/ninject-vs-unity-for-di ), pero puede ser el momento de un seguimiento.
Matthew Flaschen
2
posible duplicado de [Comparando Castle Windsor, Unity y StructureMap] ( stackoverflow.com/questions/2216684/… )
Mauricio Scheffer
@slomojo: Posible duplicado. stackoverflow.com/questions/4509458/ioc-comparisions-closed . También hay un enlace que muestra la popularidad de los IoC en la respuesta. Échale un vistazo.
dhinesh
@chibacity: lo he usado en ... 4 proyectos, los dos primeros eran realmente básicos, sin problemas, los segundos dos, Unity nos causó tantos problemas en lo que respecta a la inyección del constructor, el mantenimiento y la legibilidad. Terminamos arrancando Unity de ambos y lo reemplazamos con StructureMap, la inyección del constructor era muy simple, la configuración era limpia y mantenible. En mi tiempo personal he jugado con AutoFac, pero me parece complicado, necesito alguna documentación para entenderlo mejor. El resto solo puedo comentar sobre lo que he leído.
Phill
Un problema que tuvimos fue con SSRS, estaba fallando silenciosamente y al pasar por el código no pudimos entender por qué estaba fallando, la excepción fue ambigua. Pasamos una semana escribiendo soluciones para que funcione. Finalmente, cuando nos mudamos a StructureMap, tuvimos otro intento y, en cuestión de minutos, usando 'ObjectFactory.WhatDoIHave ()' supimos que el IoC se estaba construyendo antes de que los ensamblajes se cargaran en AppDomain, por lo que las interfaces nunca se registraron con el concreto tipos.
Phill

Respuestas:

225

Si bien una respuesta integral a esta pregunta ocupa cientos de páginas de mi libro , aquí hay una tabla de comparación rápida en la que todavía estoy trabajando:

Una tabla que explica la diferencia entre varios DIC

Mark Seemann
fuente
40
He leído el MEAP de tu libro y me he preguntado por qué has dejado a Ninject fuera.
Martin Owen
2
La respuesta parcial se puede encontrar aquí: manning-sandbox.com/thread.jspa?threadID=38943
Mark Seemann
25
@ Mark, gracias por esto, espero que tu respuesta pueda incluir Ninject (importante, no solo por el nuevo despliegue publicitario que lo rodea, sino también por el uso de nuevas características del lenguaje).
ocodo
3
Ninject es similar a AutoFac de muchas maneras, es usado por el Equipo NUGET y el contenedor IOC descargado más popular sin dudas. Me decepcionó que no estuviera en la inyección de dependencia de Mark en el libro .NET. Si hay una segunda edición en la forma en que se ve la industria, es de esperar que aparezca en el libro. O me encuentro con Unity, MEF (no un DI real), Ninject o StructurMap, simplemente todavía tengo que aterrizar en un contrato o concierto remoto que use spring.net o autofac, etc ...
Tom Stickel
2
Unity 3.5 ya admite el registro basado en convenciones: nuget.org/packages/Unity/3.5.1404 . Eliminar una desventaja ;-)
Vladimir Dorokhov
116

Me encontré con otra comparación de rendimiento (última actualización 10 de abril de 2014). Compara lo siguiente:

Aquí hay un resumen rápido de la publicación:

Conclusión

Ninject es definitivamente el contenedor más lento.

MEF, LinFu y Spring.NET son más rápidos que Ninject, pero siguen siendo bastante lentos. AutoFac, Catel y Windsor son los siguientes, seguidos de StructureMap, Unity y LightCore. Una desventaja de Spring.NET es que solo se puede configurar con XML.

SimpleInjector, Hiro, Funq, Munq y Dynamo ofrecen el mejor rendimiento, son extremadamente rápidos. ¡Pruébalos!

Especialmente el inyector simple parece ser una buena opción. Es muy rápido, tiene una buena documentación y también admite escenarios avanzados como intercepción y decoradores genéricos.

También puede intentar usar la Biblioteca de selector de servicios comunes y, con suerte, probar varias opciones y ver qué funciona mejor para usted.

Alguna información sobre la Biblioteca Selector de Servicio Común del sitio:

La biblioteca proporciona una abstracción sobre contenedores de IoC y localizadores de servicios. El uso de la biblioteca permite que una aplicación acceda indirectamente a las capacidades sin depender de referencias duras. La esperanza es que al usar esta biblioteca, las aplicaciones y los marcos de terceros puedan comenzar a aprovechar IoC / Service Location sin atarse a una implementación específica.

Actualizar

13.09.2011: Funq y Munq se agregaron a la lista de concursantes. Los gráficos también se actualizaron y Spring.NET se eliminó debido a su bajo rendimiento.

11/04/2011: "se agregó Simple Injector , el rendimiento es el mejor de todos los concursantes"

Pranav Shah
fuente
(Desde el siguiente enlace de comparación) Actualizado recientemente, interesante para ver las diferencias de velocidad (más la matriz de características básicas). Gracias.
lko
Esa comparación no es tan confiable porque, hasta donde yo sé, Ninject tiene extensiones para la configuración de Intercepción y XML, mientras que la comparación establece que no.
Daniel
15
Esta es una comparación muy cuantitativa. ¿Qué pasa con las características de no rendimiento como el tamaño del archivo o la cantidad de dependencias requeridas? Además, serían útiles medidas subjetivas como la calidad de la documentación o la usabilidad. Mi punto es que hay otros factores a considerar además de la velocidad.
FistOfFury
1
Al igual que Jeremy Miller, el autor de StructureMap ha dicho en el pasado ... parafraseando: seguro que hay contenedores de COI más rápidos, pero carecen de un conjunto completo de características.
Tom Stickel
mira
Rajesh Jinaga
49

Solo lea este gran blog de comparación de contenedores .Net DI de Philip Mat.

Realiza algunas pruebas exhaustivas de comparación de rendimiento;

Recomienda Autofac ya que es pequeño, rápido y fácil de usar ... Estoy de acuerdo. Parece que Unity y Ninject son los más lentos en sus pruebas.

Brodie
fuente
55
Hay una actualización de la publicación .Net DI Container Speed ​​Redux : En el fondo, se adoptó un enfoque incorrecto para Unity en primer lugar. Con las nuevas medidas, Unity se ve mucho mejor.
Volker von Einem
33

Descargo de responsabilidad: a principios de 2015, hay una gran comparación de las características de IoC Container de Jimmy Bogard , aquí hay un resumen:

Contenedores comparados:

  • Autofac
  • Ninject
  • Inyector simple
  • EstructuraMapa
  • Unidad
  • Windsor

El escenario es este: tengo una interfaz, IMediator, en la que puedo enviar una sola solicitud / respuesta o una notificación a múltiples destinatarios:

public interface IMediator 
{ 
    TResponse Send<TResponse>(IRequest<TResponse> request);

    Task<TResponse> SendAsync<TResponse>(IAsyncRequest<TResponse> request);

    void Publish<TNotification>(TNotification notification)
        where TNotification : INotification;

    Task PublishAsync<TNotification>(TNotification notification)
        where TNotification : IAsyncNotification; 
}

Luego creé un conjunto base de solicitudes / respuestas / notificaciones:

public class Ping : IRequest<Pong>
{
    public string Message { get; set; }
}
public class Pong
{
    public string Message { get; set; }
}
public class PingAsync : IAsyncRequest<Pong>
{
    public string Message { get; set; }
}
public class Pinged : INotification { }
public class PingedAsync : IAsyncNotification { }

Estaba interesado en ver algunas cosas con respecto al soporte de contenedores para genéricos:

  • Configuración para genéricos abiertos (registrando IRequestHandler <,> fácilmente)
  • Configuración para múltiples registros de genéricos abiertos (dos o más INotificationHandlers)

Configuración para la variación genérica (registro de controladores para INotification base / creación de canalizaciones de solicitud) Mis controladores son bastante sencillos, solo salen a la consola:

public class PingHandler : IRequestHandler<Ping, Pong> { /* Impl */ }
public class PingAsyncHandler : IAsyncRequestHandler<PingAsync, Pong> { /* Impl */ }

public class PingedHandler : INotificationHandler<Pinged> { /* Impl */ }
public class PingedAlsoHandler : INotificationHandler<Pinged> { /* Impl */ }
public class GenericHandler : INotificationHandler<INotification> { /* Impl */ }

public class PingedAsyncHandler : IAsyncNotificationHandler<PingedAsync> { /* Impl */ }
public class PingedAlsoAsyncHandler : IAsyncNotificationHandler<PingedAsync> { /* Impl */ }

Autofac

var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof (IMediator).Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof (Ping).Assembly).AsImplementedInterfaces();
  • Genéricos abiertos: sí, implícitamente
  • Múltiples genéricos abiertos: sí, implícitamente
  • Contravarianza genérica: sí, explícitamente

Ninject

var kernel = new StandardKernel();
kernel.Components.Add<IBindingResolver, ContravariantBindingResolver>();
kernel.Bind(scan => scan.FromAssemblyContaining<IMediator>()
    .SelectAllClasses()
    .BindDefaultInterface());
kernel.Bind(scan => scan.FromAssemblyContaining<Ping>()
    .SelectAllClasses()
    .BindAllInterfaces());
kernel.Bind<TextWriter>().ToConstant(Console.Out);
  • Genéricos abiertos: sí, implícitamente
  • Múltiples genéricos abiertos: sí, implícitamente
  • Contravarianza genérica: sí, con extensiones creadas por el usuario

Inyector simple

var container = new Container();
var assemblies = GetAssemblies().ToArray();
container.Register<IMediator, Mediator>();
container.Register(typeof(IRequestHandler<,>), assemblies);
container.Register(typeof(IAsyncRequestHandler<,>), assemblies);
container.RegisterCollection(typeof(INotificationHandler<>), assemblies);
container.RegisterCollection(typeof(IAsyncNotificationHandler<>), assemblies);
  • Genéricos abiertos: sí, explícitamente
  • Múltiples genéricos abiertos: sí, explícitamente
  • Contravarianza genérica: sí, implícitamente (con la actualización 3.0)

EstructuraMapa

var container = new Container(cfg =>
{
    cfg.Scan(scanner =>
    {
        scanner.AssemblyContainingType<Ping>();
        scanner.AssemblyContainingType<IMediator>();
        scanner.WithDefaultConventions();
        scanner.AddAllTypesOf(typeof(IRequestHandler<,>));
        scanner.AddAllTypesOf(typeof(IAsyncRequestHandler<,>));
        scanner.AddAllTypesOf(typeof(INotificationHandler<>));
        scanner.AddAllTypesOf(typeof(IAsyncNotificationHandler<>));
    });
});
  • Genéricos abiertos: sí, explícitamente
  • Múltiples genéricos abiertos: sí, explícitamente
  • Contravarianza genérica: sí, implícitamente

Unidad

container.RegisterTypes(AllClasses.FromAssemblies(typeof(Ping).Assembly),
   WithMappings.FromAllInterfaces,
   GetName,
   GetLifetimeManager);

/* later down */

static bool IsNotificationHandler(Type type)
{
    return type.GetInterfaces().Any(x => x.IsGenericType && (x.GetGenericTypeDefinition() == typeof(INotificationHandler<>) || x.GetGenericTypeDefinition() == typeof(IAsyncNotificationHandler<>)));
}

static LifetimeManager GetLifetimeManager(Type type)
{
    return IsNotificationHandler(type) ? new ContainerControlledLifetimeManager() : null;
}

static string GetName(Type type)
{
    return IsNotificationHandler(type) ? string.Format("HandlerFor" + type.Name) : string.Empty;
}
  • Genéricos abiertos: sí, implícitamente
  • Múltiples genéricos abiertos: sí, con extensión creada por el usuario
  • Contravarianza genérica: derp

Windsor

var container = new WindsorContainer();
container.Register(Classes.FromAssemblyContaining<IMediator>().Pick().WithServiceAllInterfaces());
container.Register(Classes.FromAssemblyContaining<Ping>().Pick().WithServiceAllInterfaces());
container.Kernel.AddHandlersFilter(new ContravariantFilter());
  • Genéricos abiertos: sí, implícitamente
  • Múltiples genéricos abiertos: sí, implícitamente
  • Contravarianza genérica: sí, con extensión creada por el usuario
stratovarius
fuente
¡Excelente! Por cierto, el resumen anterior se perdió Windsor, pero está disponible en el artículo original de Jimmy.
Louis
Wow, nadie lo advirtió antes (: agregué windsor, gracias @Louis
stratovarius el
21

En realidad, hay toneladas de marcos IoC. Parece que cada programador intenta escribir uno en algún momento de su carrera. Quizás no para publicarlo, sino para aprender el funcionamiento interno.

Personalmente prefiero autofac ya que es bastante flexible y tiene una sintaxis que me conviene (aunque realmente odio que todos los métodos de registro sean métodos de extensión).

Algunos otros marcos:

jgauffin
fuente
Hola @abatishchev! :) ... la idea original era asegurarse de que los métodos integrados y de terceros estuvieran en la misma base; muchos métodos de "registro" tienen que enviarse por separado (por ejemplo, RegisterControllers()para MVC), así que pensé que valía la pena diseñar alrededor de ese caso. (Esto fue diseñado hace más de 5 años.)
Nicholas Blumhardt
1
@NicholasBlumhardt: ¡Hola! :) Perdón por la respuesta tardía, la notificación se perdió entre los demás. En realidad, ese motivo de coherencia tiene sentido para mí. ¿Cómo piensas ahora, cómo lo diseñarías?
abatishchev
@abatishchev No estoy de acuerdo con jgauffin. Los métodos de extensión no están cerrados por extensión, son extensión. Escribe el núcleo de su marco de trabajo que puede hacer todo lo que debería y con los métodos de extensión proporciona alguna funcionalidad adicional, tal vez algunos ayudantes predeterminados, pero cualquier otra persona es libre de escribir sus propias extensiones. Diría que si su marco acepta métodos de extensión para extenderlo, entonces es un buen marco.
t3chb0t
6

Bueno, después de buscar la mejor comparación que he encontrado hasta ahora es:

Fue una encuesta realizada en marzo de 2010.

Un punto de interés para mí es que las personas que han usado un Marco DI / IoC y les ha gustado / no les ha gustado, StructureMap parece estar en la cima.

También de la encuesta, parece que Castle.Windsor y StructureMap parecen ser los más favorecidos.

Curiosamente, Unity y Spring.Net parecen ser las opciones populares que generalmente no están de acuerdo. (Estaba considerando Unity por pereza (y soporte / insignia de Microsoft), pero ahora estaré mirando más de cerca a Castle Windsor y StructureMap).

Por supuesto, esto probablemente (?) No se aplica a Unity 2.0, que se lanzó en mayo de 2010.

Esperemos que alguien más pueda proporcionar una comparación basada en la experiencia directa.

ocodo
fuente
2
La unidad es bastante buena. Cubre la mayor parte de lo que uno necesita, aunque algunas personas se quejan de que no resuelve dependencias circulares. Me encanta. Hago todo lo que necesito.
Dmitri Nesteruk
Muchos desarrolladores están usando Castle.Windsor sin siquiera saberlo. Es el Ioc predeterminado para NHibernate . (Al menos con el FluentNHibernate que descargué ayer). También vi una implementación de NHibernate que usa LinFu nstead
k3b
5

Consulte para una comparación de net-ioc-frameworks en el código de google, incluidos linfu y spring.net que no están en su lista mientras escribo este texto.

Trabajé con spring.net: tiene muchas características (aop, bibliotecas, documentos, ...) y hay mucha experiencia en dotnet y el mundo java. Las características están modularizadas para que no tenga que tomar todas las características. Las características son abstracciones de problemas comunes como la extracción de bases de datos, la extracción de registros. sin embargo, es difícil hacer y depurar la configuración de IoC.

Por lo que he leído hasta ahora: si tuviera que elegirh para un proyecto pequeño o mediano, usaría ninject ya que la configuración ioc está hecha y se puede depurar en c #. Pero todavía no he trabajado con eso. para un sistema modular grande, me quedaría con spring.net debido a las bibliotecas de abstracción.

k3b
fuente