Como as principais estruturas C / DI / IoC se comparam? [fechadas]

308

Correndo o risco de entrar no território da guerra santa, quais são os pontos fortes e fracos dessas estruturas populares de DI / IoC e se poderia facilmente ser considerado o melhor? ..:

  • Ninject
  • Unidade
  • Castle.Windsor
  • Autofac
  • StructureMap

Existem outras estruturas de DI / IoC para C # que não listei aqui?

No contexto do meu caso de uso, estou criando um aplicativo cliente WPF e uma infraestrutura de serviços WCF / SQL, facilidade de uso (especialmente em termos de sintaxe clara e concisa), documentação consistente, bom suporte e desempenho da comunidade são fatores importantes na minha escolha.

Atualizar:

Os recursos e as perguntas duplicadas citadas parecem estar desatualizados. Alguém com conhecimento de todas essas estruturas pode apresentar e fornecer algumas informações reais?

Sei que é provável que a maioria das opiniões sobre esse assunto seja tendenciosa, mas espero que alguém tenha estudado todas essas estruturas e tenha pelo menos uma comparação geralmente objetiva.

Estou bastante disposto a fazer minhas próprias investigações se isso não tiver sido feito antes, mas presumi que isso fosse algo que pelo menos algumas pessoas já haviam feito.

Segunda atualização:

Se você tem experiência com mais de um contêiner de DI / IoC, classifique e resuma os prós e os contras deles, obrigado. Este não é um exercício para descobrir todos os pequenos recipientes obscuros que as pessoas criaram. Estou procurando comparações entre as estruturas populares (e ativas).

ocodo
fonte
1
Mesma pergunta que [Ninject vs Unity for DI] ( stackoverflow.com/questions/1054801/ninject-vs-unity-for-di ), mas talvez seja hora de fazer um acompanhamento.
Matthew Flaschen
2
possível duplicação de [Comparando Castle Windsor, Unity e StructureMap] ( stackoverflow.com/questions/2216684/… )
Mauricio Scheffer
@ Slomojo: Possível duplicado. stackoverflow.com/questions/4509458/ioc-comparisions-closed . Também há um link que mostra a popularidade dos IoC na resposta. Dê uma olhada nisso.
dhinesh
@chibacity - eu usei em ... 4 projetos, os dois primeiros foram realmente básicos, sem problemas, os dois primeiros, o Unity nos causou muitos problemas quando se tratava de injeção de construtor, manutenibilidade, legibilidade. Acabamos retirando o Unity dos dois e o substituindo pelo StructureMap, a injeção do construtor era simples, a configuração era limpa e sustentável. No meu tempo pessoal, eu brinquei com o AutoFac, mas acho complicado, preciso de alguma documentação para entender melhor. O resto só posso comentar sobre o que li.
Phill
Um problema que tivemos foi com o SSRS: ele falhou silenciosamente e percorreu o código que não conseguimos descobrir por que estava falhando; a exceção era ambígua. Passamos uma semana escrevendo soluções alternativas para fazê-lo funcionar. Eventualmente, quando mudamos para o StructureMap, tivemos outra tentativa e, em minutos, usando 'ObjectFactory.WhatDoIHave ()', descobrimos que o IoC estava sendo construído antes que os assemblies fossem carregados no AppDomain, para que as interfaces nunca fossem registradas com o concreto tipos.
Phill

Respostas:

225

Embora uma resposta abrangente a essa pergunta ocupe centenas de páginas do meu livro , aqui está um gráfico de comparação rápida em que ainda estou trabalhando:

Uma tabela explicando a diferença entre vários DICs

Mark Seemann
fonte
40
Eu li o MEAP do seu livro e me pergunto por que você deixou o Ninject fora dele?
Martin Owen
2
Resposta parcial pode ser encontrada aqui: manning-sandbox.com/thread.jspa?threadID=38943
Mark Seemann
25
@ Marcos, obrigado por isso, espero que sua resposta poderia incluir Ninject (importante, não só por causa do novo hype em torno dela, mas também devido à sua utilização de novos recursos de linguagem.)
ocodo
3
O Ninject, embora similar ao AutoFac, seja de várias maneiras, é usado pela Equipe NUGET e o mais popular recipiente de IOC baixado. Fiquei desapontado por não constar no livro Injeção de Dependência de Mark no .NET. Se houver uma 2ª edição na aparência do setor, espero que consiga entrar no livro. Ou eu correr em Unity, MEF (não um real DI), Ninject, ou StructurMap, eu simplesmente tem ainda à terra em um contrato ou show remoto que usa spring.net ou Autofac etc ...
Tom Stickel
2
O Unity 3.5 já suporta registro baseado em convenção: nuget.org/packages/Unity/3.5.1404 . Remover uma desvantagem ;-)
Vladimir Dorokhov
116

Me deparei com outra comparação de desempenho (última atualização em 10 de abril de 2014). Ele compara o seguinte:

Aqui está um rápido resumo do post:

Conclusão

Ninject é definitivamente o recipiente mais lento.

MEF, LinFu e Spring.NET são mais rápidos que o Ninject, mas ainda bem lentos. AutoFac, Catel e Windsor são os próximos, seguidos por StructureMap, Unity e LightCore. Uma desvantagem do Spring.NET é que isso só pode ser configurado com XML.

SimpleInjector, Hiro, Funq, Munq e Dynamo oferecem o melhor desempenho, eles são extremamente rápidos. Experimente!

O Injetor especialmente simples parece ser uma boa escolha. É muito rápido, possui uma boa documentação e também suporta cenários avançados como interceptação e decoradores genéricos.

Você também pode tentar usar a Common Service Selector Library e, esperançosamente, tentar várias opções e ver o que funciona melhor para você.

Algumas informações sobre o Common Service Selector Library do site:

A biblioteca fornece uma abstração sobre contêineres IoC e localizadores de serviço. O uso da biblioteca permite que um aplicativo acesse indiretamente os recursos sem depender de referências concretas. A esperança é que, usando essa biblioteca, aplicativos e estruturas de terceiros possam começar a alavancar o IoC / Service Location sem se limitar a uma implementação específica.

Atualizar

13.09.2011: Funq e Munq foram adicionados à lista de participantes. Os gráficos também foram atualizados e o Spring.NET foi removido devido ao seu baixo desempenho.

11.04.2011: "adicionado Injetor Simples , o desempenho é o melhor de todos os participantes".

Pranav Shah
fonte
(Seguindo o link de comparação) Atualizado recentemente, interessante ver as diferenças de velocidade (mais a matriz de recursos básicos). Obrigado.
Lko 19/04
Essa comparação não é tão confiável, porque, até onde eu sei, o Ninject possui extensões para configurações de interceptação e XML, enquanto a comparação afirma que não.
Daniel
15
esta é uma comparação muito quantitativa. e os recursos de não desempenho, como o tamanho do arquivo ou o número de dependências necessárias? Além disso, medidas subjetivas, como qualidade ou usabilidade da documentação, seriam úteis. O que quero dizer é que há outros fatores a serem considerados além da velocidade.
FistOfFury
1
Como Jeremy Miller, o autor do StructureMap disse no passado ... parafraseando - que com certeza existem contêineres IOC mais rápidos, mas eles não têm um conjunto completo de recursos.
precisa
Confira isto: iocservicestack.net
Rajesh Jinaga
49

Basta ler este ótimo blog de comparação de contêineres .Net DI de Philip Mat.

Ele faz alguns testes completos de comparação de desempenho;

Ele recomenda o Autofac , pois é pequeno, rápido e fácil de usar ... Concordo. Parece que Unity e Ninject são os mais lentos em seus testes.

brodie
fonte
5
Há uma atualização no post. Redux de velocidade de contêineres .Net DI : No final das contas, houve uma abordagem incorreta para o Unity em primeiro lugar. Com as novas medições, o Unity parece muito melhor.
Volker von Einem 23/03
33

Isenção de responsabilidade: Desde o início de 2015, há uma excelente comparação dos recursos de IoC Container de Jimmy Bogard , aqui está um resumo:

Recipientes comparados:

  • Autofac
  • Ninject
  • Injetor Simples
  • StructureMap
  • Unidade
  • Windsor

O cenário é o seguinte: Eu tenho uma interface, IMediator, na qual posso enviar uma única solicitação / resposta ou uma notificação para vários destinatários:

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; 
}

Criei um conjunto básico de solicitações / respostas / notificações:

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 { }

Eu estava interessado em examinar algumas coisas em relação ao suporte a contêiner para genéricos:

  • Configuração para genéricos abertos (registrando IRequestHandler <,> facilmente)
  • Configuração para vários registros de genéricos abertos (dois ou mais INotificationHandlers)

Configuração para variação genérica (registrando manipuladores para INotification base / criando pipelines de solicitação) Meus manipuladores são bem diretos, eles apenas enviam para o console:

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 abertos: sim, implicitamente
  • Vários genéricos abertos: sim, implicitamente
  • Contravariância genérica: sim, explicitamente

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 abertos: sim, implicitamente
  • Vários genéricos abertos: sim, implicitamente
  • Contravariância genérica: sim, com extensões criadas pelo usuário

Injetor Simples

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 abertos: sim, explicitamente
  • Vários genéricos abertos: sim, explicitamente
  • Contravariância genérica: sim, implicitamente (com atualização 3.0)

StructureMap

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 abertos: sim, explicitamente
  • Vários genéricos abertos: sim, explicitamente
  • Contravariância genérica: sim, implicitamente

Unidade

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 abertos: sim, implicitamente
  • Vários genéricos abertos: sim, com extensão criada pelo usuário
  • Contravariância 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 abertos: sim, implicitamente
  • Vários genéricos abertos: sim, implicitamente
  • Contravariância genérica: sim, com extensão criada pelo usuário
estratovarius
fonte
Excelente! Aliás, o resumo acima perdeu o Windsor, mas está disponível no artigo original de Jimmy.
Louis
Uau ninguém avisou sobre isso antes (: Eu adicionei Windsor, graças @Louis
stratovarius
21

Na verdade, existem toneladas de estruturas de IoC. Parece que todo programador tenta escrever um em algum momento de sua carreira. Talvez não para publicá-lo, mas para aprender o funcionamento interno.

Pessoalmente, prefiro o autofac, pois ele é bastante flexível e possui uma sintaxe que combina comigo (embora eu realmente odeie que todos os métodos de registro sejam métodos de extensão).

Algumas outras estruturas:

jgauffin
fonte
Oi @abatishchev! :) ... a idéia original era garantir que métodos internos e de terceiros estivessem no mesmo pé; muitos métodos de "registro" precisam ser enviados separadamente (por exemplo, RegisterControllers()para MVC), então pensei que o design em torno desse caso valia a pena. (Isto foi projetado 5+ anos atrás.)
Nicholas Blumhardt
1
@NicholasBlumhardt: Oi! :) Desculpe pela resposta tardia, a notificação se perdeu entre os outros. Na verdade, essa questão de consistência faz sentido para mim. Como você pensa agora, como você o projetaria?
precisa saber é o seguinte
@abatishchev Não concordo com o jgauffin. Os métodos de extensão não estão fechados para extensão, eles são extensão. Você escreve o núcleo da sua estrutura que pode fazer tudo o que deveria e, com os métodos de extensão, fornece algumas funcionalidades adicionais, talvez alguns auxiliares padrão, mas qualquer outra pessoa é livre para escrever suas próprias extensões. Eu diria que se o seu framework aceita métodos de extensão para estendê-lo, é um bom framework.
T3chb0t
6

Bem, depois de procurar a melhor comparação que encontrei até agora, é:

Foi uma pesquisa realizada em março de 2010.

Um ponto de interesse para mim é que as pessoas que usaram um DI / IoC Framework e gostaram / não gostaram, o StructureMap parece estar no topo.

Também da pesquisa, parece que Castle.Windsor e StructureMap parecem ser os mais favorecidos.

Curiosamente, Unity e Spring.Net parecem ser as opções populares que geralmente não são apreciadas. (Eu estava pensando em Unity por preguiça (e crachá / suporte da Microsoft), mas examinarei mais de perto o Castle Windsor e o StructureMap agora.)

É claro que isso provavelmente (?) Não se aplica ao Unity 2.0, lançado em maio de 2010.

Espero que alguém possa fornecer uma comparação com base na experiência direta.

ocodo
fonte
2
A união é muito boa. Ele cobre a maior parte do que é necessário, embora algumas pessoas se queixem de que não resolve dependências circulares. Eu amo isso. Eu faço tudo o que preciso.
Dmitri Nesteruk
Muitos desenvolvedores estão usando o Castle.Windsor sem nem mesmo saber. É o Ioc padrão do NHibernate . (Pelo menos com o FluentNHibernate eu fiz downlod ontem). Eu também vi uma implementação NHibernate que usos LinFu nstead
k3b
5

Veja uma comparação dos frameworks net-ioc-framework no código do Google, incluindo linfu e spring.net, que não estão na sua lista enquanto escrevo este texto.

Eu trabalhei com o spring.net: ele tem muitos recursos (aop, bibliotecas, docu, ...) e há muita experiência com ele no dotnet e no mundo java. Os recursos são modularizados para que você não precise usar todos os recursos. Os recursos são abstrações de problemas comuns, como banco de dados e abstração, registro e abstração. no entanto, é difícil fazer e depurar a configuração de IoC.

Pelo que li até agora: Se eu tivesse que escolher um projeto pequeno ou médio, usaria o ninject, pois a configuração do ioc é feita e depurável em c #. Mas ainda não trabalhei com isso. para sistemas modulares grandes, eu ficaria com o spring.net por causa das bibliotecas de abstração.

k3b
fonte