Jak porównywane są główne frameworki C # DI / IoC? [Zamknięte]

308

Ryzykując wkroczenie na terytorium świętej wojny, jakie są mocne i słabe strony tych popularnych platform DI / IoC i czy można je łatwo uznać za najlepsze? ..:

  • Ninject
  • Jedność
  • Castle.Windsor
  • Autofac
  • StrukturaMapa

Czy istnieją inne frameworki DI / IoC dla C #, których tutaj nie wymieniłem?

W kontekście mojego przypadku użycia buduję aplikację kliencką WPF i infrastrukturę usług WCF / SQL, łatwość użycia (szczególnie pod względem przejrzystej i zwięzłej składni), spójną dokumentację, dobre wsparcie społeczności i wydajność - wszystko to są ważne czynniki według mojego wyboru.

Aktualizacja:

Przytoczone zasoby i zduplikowane pytania wydają się nieaktualne. Czy ktoś ze znajomością wszystkich tych ram może się zgłosić i zapewnić prawdziwy wgląd?

Zdaję sobie sprawę, że większość opinii na ten temat może być stronnicza, ale mam nadzieję, że ktoś poświęcił czas na przestudiowanie wszystkich tych ram i co najmniej ogólnie obiektywne porównanie.

Jestem skłonny przeprowadzić własne dochodzenie, jeśli nie zostało to zrobione wcześniej, ale założyłem, że było to coś, co zrobiło już kilka osób.

Druga aktualizacja:

Jeśli masz doświadczenie z więcej niż jednym kontem DI / IoC, proszę uszeregować i podsumować zalety i wady tych kontenerów, dziękuję. Nie jest to ćwiczenie polegające na odkrywaniu wszystkich niejasnych małych kontenerów, które ludzie stworzyli, szukam porównań między popularnymi (i aktywnymi) frameworkami.

ocodo
źródło
1
To samo pytanie, co [Ninject vs Unity for DI] ( stackoverflow.com/questions/1054801/ninject-vs-unity-for-di ), ale może to być czas na kontynuację.
Matthew Flaschen
2
możliwy duplikat [Porównania Zamku Windsor, Jedności i StrukturyMapy] ( stackoverflow.com/questions/2216684/... )
Mauricio Scheffer
@slomojo: Możliwy duplikat. stackoverflow.com/questions/4509458/ioc-comparisions-closed . Istnieje również link, który pokazuje popularność IoC w odpowiedzi. Spójrz na to.
dhinesh
@ chibacity - użyłem go na ... 4 projektach, pierwsze dwa były naprawdę podstawowe, żadnych problemów, drugie dwa, Unity sprawiło nam tyle problemów, jeśli chodzi o wstrzyknięcie konstruktora, łatwość konserwacji, czytelność. Ostatecznie wyjęliśmy Unity z obu i zastąpiliśmy go StructureMap, wstrzyknięcie konstruktora było bardzo proste, konfiguracja była czysta i możliwa do utrzymania. Prywatnie grałem z AutoFac, ale uważam to za kłopotliwe, potrzebuję trochę dokumentacji, aby lepiej to zrozumieć. Resztę mogę komentować tylko to, co przeczytałem.
Phill
Jeden problem, jaki mieliśmy, dotyczył SSRS, po prostu nie działał i przechodząc przez kod, nie mogliśmy zrozumieć, dlaczego zawodzi, wyjątek był niejednoznaczny. Spędziliśmy tydzień pisząc obejścia, żeby to działało. W końcu, kiedy przenieśliśmy się do StructureMap, podjęliśmy kolejną próbę. W ciągu kilku minut za pomocą „ObjectFactory.WhatDoIHave ()” dowiedzieliśmy się, że IoC budowano, zanim zespoły zostały załadowane do AppDomain, więc interfejsy nigdy nie zostały zarejestrowane w konkretnym typy.
Phill

Odpowiedzi:

225

Chociaż wyczerpująca odpowiedź na to pytanie zajmuje setki stron mojej książki , oto krótka tabela porównawcza, nad którą wciąż pracuję:

Tabela wyjaśniająca różnicę między kilkoma DIC

Mark Seemann
źródło
40
Przeczytałem MEAP z twojej książki i zastanawiałem się, dlaczego opuściłeś Ninject?
Martin Owen,
2
Częściową odpowiedź można znaleźć tutaj: manning-sandbox.com/thread.jspa?threadID=38943
Mark Seemann
25
@Mark, dzięki za to, mam nadzieję, że twoja odpowiedź może zawierać Ninject (ważne, nie tylko ze względu na nowy szum wokół niego, ale także ze względu na wykorzystanie nowych funkcji językowych.)
ocodo
3
Ninject, choć podobny do AutoFac, jest pod wieloma względami, jest używany przez zespół NUGET, a najpopularniejszy pobrany kontener IOC jest dostępny. Byłem rozczarowany, że nie było tego w Mark's Dependency Injection w książce .NET. Jeśli jest druga edycja w wyglądzie branży, mam nadzieję, że trafiłaby do książki. Albo wpadam na Unity, MEF (nie jest to prawdziwy DI), Ninject lub StructurMap, po prostu muszę jeszcze wylądować na kontrakcie lub zdalnym koncercie, który korzysta z spring.net lub autofac itp.
Tom Stickel
2
Unity 3.5 obsługuje już rejestrację opartą na konwencjach: nuget.org/packages/Unity/3.5.1404 . Usuń jedną wadę ;-)
Vladimir Dorokhov
116

Natknąłem się na inne porównanie wydajności (najnowsza aktualizacja 10 kwietnia 2014 r.). Porównuje następujące:

Oto krótkie podsumowanie postu:

Wniosek

Ninject jest zdecydowanie najwolniejszym pojemnikiem.

MEF, LinFu i Spring.NET są szybsze niż Ninject, ale wciąż dość powolne. Następne są AutoFac, Catel i Windsor, a następnie StructureMap, Unity i LightCore. Wadą Spring.NET jest to, że można go skonfigurować tylko za pomocą XML.

SimpleInjector, Hiro, Funq, Munq i Dynamo oferują najlepszą wydajność, są niezwykle szybkie. Wypróbuj je!

Szczególnie prosty wtryskiwacz wydaje się dobrym wyborem. Jest bardzo szybki, ma dobrą dokumentację, a także obsługuje zaawansowane scenariusze, takie jak przechwytywanie i ogólne dekoratory.

Możesz także spróbować skorzystać z biblioteki Common Service Selector Library i, mam nadzieję, wypróbować wiele opcji i sprawdzić, co będzie dla Ciebie najlepsze.

Niektóre informacje o Common Selector Library z witryny:

Biblioteka zapewnia abstrakcję kontenerów IoC i lokalizatorów usług. Korzystanie z biblioteki pozwala aplikacji na pośredni dostęp do możliwości bez konieczności korzystania z twardych referencji. Mamy nadzieję, że dzięki tej bibliotece aplikacje i frameworki innych firm mogą zacząć wykorzystywać IoC / Service Location bez ograniczania się do konkretnej implementacji.

Aktualizacja

13.09.2011: Funq i Munq zostali dodani do listy uczestników. Wykresy również zostały zaktualizowane, a Spring.NET został usunięty z powodu niskiej wydajności.

04.11.2011: „dodano Simple Injector , wydajność jest najlepsza ze wszystkich zawodników”.

Pranav Shah
źródło
(Z poniższego linku porównawczego) Ostatnio zaktualizowane, interesujące, aby zobaczyć różnice prędkości (plus matrycę podstawowych funkcji). Dzięki.
lko
To porównanie nie jest tak wiarygodne, ponieważ o ile wiem Ninject ma rozszerzenia zarówno do Przechwytywania, jak i konfiguracji XML, podczas gdy porównanie stwierdza, że ​​nie.
Daniel
15
jest to bardzo ilościowe porównanie. co z funkcjami braku wydajności, takimi jak rozmiar pliku lub wymagana liczba zależności? Ponadto pomocne byłyby subiektywne miary, takie jak jakość dokumentacji lub użyteczność. Chodzi mi o to, że należy wziąć pod uwagę inne czynniki niż prędkość.
FistOfFury
1
Podobnie jak Jeremy Miller, autor StructureMap powiedział w przeszłości ... parafrazując - z pewnością istnieją szybsze pojemniki IOC, ale brakuje im pełnego zestawu funkcji.
Tom Stickel
sprawdź to: iocservicestack.net
Rajesh Jinaga
49

Wystarczy przeczytać ten wspaniały blog z porównaniem kontenerów .Net DI autorstwa Philipa Mat.

Przeprowadza dokładne testy porównawcze wydajności;

Poleca Autofac, ponieważ jest mały, szybki i łatwy w użyciu ... Zgadzam się. Wygląda na to, że Unity i Ninject są najwolniejsze w jego testach.

brodie
źródło
5
Aktualizacja wpisu .Net DI Container Speed ​​Redux : W dolnej linii, w pierwszej kolejności zastosowano niewłaściwe podejście do Unity. Dzięki nowym pomiarom Unity wygląda znacznie lepiej.
Volker von Einem
33

Oświadczenie: Na początku 2015 r. Istnieje świetne porównanie funkcji kontenera IoC od Jimmy'ego Bogarda , oto podsumowanie:

Porównywane pojemniki:

  • Autofac
  • Ninject
  • Prosty wtryskiwacz
  • StrukturaMapa
  • Jedność
  • Windsor

Scenariusz jest następujący: mam interfejs IMediator, w którym mogę wysłać pojedyncze żądanie / odpowiedź lub powiadomienie do wielu odbiorców:

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

Następnie utworzyłem podstawowy zestaw żądań / odpowiedzi / powiadomień:

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

Chciałem przyjrzeć się kilku kwestiom związanym z obsługą kontenerów dla leków generycznych:

  • Konfiguracja dla otwartych generics (łatwa rejestracja IRequestHandler <,>)
  • Konfiguracja wielu rejestracji otwartych generycznych (dwóch lub więcej INotificationHandler)

Konfiguracja wariancji ogólnej (rejestrowanie procedur obsługi dla podstawowego INotification / tworzenie potoków żądań) Moje procedury obsługi są dość proste, po prostu wysyłają dane do konsoli:

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();
  • Otwarte leki generyczne: tak, domyślnie
  • Wiele otwartych rodzajów ogólnych: tak, domyślnie
  • Kontrawariancja ogólna: tak, wyraźnie

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);
  • Otwarte leki generyczne: tak, domyślnie
  • Wiele otwartych rodzajów ogólnych: tak, domyślnie
  • Kontrawariancja ogólna: tak, z rozszerzeniami zbudowanymi przez użytkownika

Prosty wtryskiwacz

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);
  • Otwarte generyczne: tak, wyraźnie
  • Wiele otwartych rodzajów ogólnych: tak, wyraźnie
  • Kontrawariancja ogólna: tak, niejawnie (z aktualizacją 3.0)

StrukturaMapa

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<>));
    });
});
  • Otwarte generyczne: tak, wyraźnie
  • Wiele otwartych rodzajów ogólnych: tak, wyraźnie
  • Kontrawariancja ogólna: tak, domyślnie

Jedność

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;
}
  • Otwarte leki generyczne: tak, domyślnie
  • Wiele otwartych ogólnych: tak, z wbudowanym rozszerzeniem
  • Kontrawariancja ogólna: 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());
  • Otwarte leki generyczne: tak, domyślnie
  • Wiele otwartych rodzajów ogólnych: tak, domyślnie
  • Kontrawariancja ogólna: tak, z wbudowanym rozszerzeniem
stratowariusz
źródło
Świetny! Btw, powyższe podsumowanie brakowało Windsora, ale jest dostępne w oryginalnym artykule Jimmy'ego.
Louis
Wow, nikt przed tym nie ostrzegał (: dodałem windsor, dzięki @Louis
stratovarius
21

W rzeczywistości istnieje mnóstwo frameworków IoC. Wygląda na to, że każdy programista próbuje napisać jeden w pewnym momencie swojej kariery. Może nie po to, by je opublikować, ale po to, aby nauczyć się wewnętrznych mechanizmów.

Osobiście wolę autofac, ponieważ jest dość elastyczny i ma odpowiednią składnię (chociaż naprawdę nie znoszę, że wszystkie metody rejestrowania są metodami rozszerzenia).

Niektóre inne frameworki:

jgauffin
źródło
Cześć @abatishchev! :) ... pierwotnym pomysłem było upewnienie się, że metody zewnętrzne i wbudowane były na tej samej podstawie; wiele metod „rejestracji” musi być wysyłanych osobno (np. RegisterControllers()dla MVC), więc pomyślałem, że warto było zaprojektować tę skrzynkę. (Zostało to zaprojektowane ponad 5 lat temu.)
Nicholas Blumhardt,
1
@NicholasBlumhardt: Cześć! :) Przepraszam za spóźnioną odpowiedź, powiadomienie zginęło wśród innych. Właściwie taka spójność ma dla mnie sens. Jak myślisz teraz, jak byś to zaprojektował?
abatishchev
@abatishchev Nie zgadzam się z jgauffin. Metody rozszerzenia nie są zamknięte dla rozszerzenia, są rozszerzeniem. Piszesz rdzeń swojego frameworka, który może zrobić wszystko, co powinien, a metodami rozszerzeń zapewniasz dodatkowe funkcje, być może niektóre domyślne pomocniki, ale każdy inny może pisać własne rozszerzenia. Powiedziałbym, że jeśli twój framework akceptuje metody rozszerzenia w celu jego rozszerzenia, to jest to dobry framework.
t3chb0t
6

Po sprawdzeniu najlepszego porównania, jakie do tej pory znalazłem, jest:

To była ankieta przeprowadzona w marcu 2010 r.

Jednym z punktów zainteresowania mnie jest to, że ludzie, którzy korzystali ze środowiska DI / IoC i polubili go / nie lubili, wydaje się, że StructureMap wychodzi na wierzch.

Także z ankiety wynika, że Castle.Windsor i StructureMap wydają się być najbardziej uprzywilejowane.

Co ciekawe, Unity i Spring.Net wydają się być popularnymi opcjami, które w większości nie są lubiane. (Rozważałem Unity z lenistwa (i odznaki / wsparcia Microsoft), ale teraz przyjrzę się bliżej Castle Windsor i StructureMap.)

Oczywiście to prawdopodobnie (?) Nie dotyczy Unity 2.0, który został wydany w maju 2010 roku.

Mam nadzieję, że ktoś inny zapewni porównanie oparte na bezpośrednim doświadczeniu.

ocodo
źródło
2
Jedność jest całkiem dobra. Obejmuje większość tego, czego potrzeba, choć niektórzy narzekają na to, że nie rozwiązuje zależności cyklicznych. Kocham to. Robię wszystko, czego potrzebuję.
Dmitri Nesteruk
Wielu programistów korzysta z Castle.Windsor nawet o tym nie wiedząc. Jest to domyślny Ioc dla NHibernate . (Przynajmniej wczoraj z FluentNHibernate i downloded). Widziałem także implementację NHibernate, która używa LinFu nstead
k3b
5

Zobacz porównanie frameworków net-ioc w kodzie Google, w tym linfu i spring.net, których nie ma na liście podczas pisania tego tekstu.

Pracowałem z spring.net: ma wiele funkcji (aop, biblioteki, docu, ...) i jest z nim wiele doświadczenia w dotnet i java-world. Funkcje są modułowe, więc nie musisz brać wszystkich funkcji. Funkcje te są abstrakcjami typowych problemów, takich jak analiza bazy danych, rejestrowanie analizy. jednak trudno jest wykonać i debugować konfigurację IoC.

Z tego, co przeczytałem do tej pory: Gdybym musiał wybierać dla małego lub średniego projektu, użyłbym ninject, ponieważ konfiguracja ioc jest wykonywana i debuggowana w języku c #. Ale jeszcze z tym nie pracowałem. dla dużego systemu modułowego zostałbym z spring.net z powodu bibliotek abstrakcji.

k3b
źródło