2017-12-06 1 views
2

Angenommen, ich habe einen Dienst, den ich mit einer Gültigkeitsdauer auflösen möchte. Aber irgendwann versuche ich es als Schnittstellentyp und manchmal als Implementierungstyp aufzulösen.ASP.NET Core DI: Lösen Sie die gleiche Instanz auf, wenn der Bereichsdienst sowohl als Servicetyp als auch als Implementierungstyp registriert ist

Das erste, was ich versucht war, dies zu tun:

ServiceCollection services; 
services.AddScoped<MyClass>(); 
services.AddScoped<IMyInterface, MyClass>(); 

Das Problem mit dem obigen Beispiel ist, dass eine andere Instanz verwendet, wenn ich IMyInterface zu lösen, und als MyClass zu lösen. Grundsätzlich ist es möglich, dass 2 Scoped-Instanzen gleichzeitig zur Verfügung stehen.

Ich umgehen dieses Problem auf folgende Weise. Aber es ist sehr fehleranfällig, weil Sie leicht vergessen können, dies an einer Stelle zu tun, und es ist wirklich schwer zu bemerken.

serviceCollection.AddScoped<MyClass>(); 
serviceCollection.AddScoped<IMyInterface, MyClass>(sp => sp.GetRequiredService<MyClass>()); 

Gibt es eine Möglichkeit zu erreichen, was ich in einer Art und Weise wollen, die weniger fehleranfällig ist. Vorzugsweise, aber nicht notwendigerweise, in einer einzigen Registrierung?

I.e. als xUnit Test:

public class Tests 
{ 
    [Fact] 
    public void ReturnsSameInstanceForImplementationAndServiceType() 
    { 
     var serviceCollection = new ServiceCollection(); 

     // TODO: Change these lines so they're less error prone. 
     serviceCollection.AddScoped<MyClass>(); 
     serviceCollection.AddScoped<IMyInterface, MyClass>(sp => sp.GetRequiredService<MyClass>()); 

     var services = serviceCollection.BuildServiceProvider(); 
     var myInt = services.GetRequiredService<IMyInterface>(); 
     var myCls = services.GetRequiredService<MyClass>(); 

     Assert.Equal(myCls, myInt); 
    } 

    class MyClass : IMyInterface { } 
    interface IMyInterface { } 
} 
+3

Warum registrieren Sie die Implementierung überhaupt? –

+0

@CamiloTerevinto Während ich zustimme, ist es nicht beste Praxis. Ich arbeite mit einer alten und großen Codebasis, wo die Implementierung an vielen Stellen erfolgt. Nicht wirklich in der Lage, dies an dieser Stelle an die Schnittstelle zu ändern. –

Antwort

5

Eine Möglichkeit wäre eine eigene Extension-Methode zu erstellen, die die beiden Linien hüllen Sie in Ihrer Frage gezeigt haben. Zum Beispiel:

public static class ServiceCollectionExtensions 
{ 
    public static void AddScopedInterfaceAndClass<TInterface, TClass>(this IServiceCollection serviceCollection) 
     where TInterface : class 
     where TClass : class, TInterface 
    { 
     serviceCollection.AddScoped<TClass>(); 
     serviceCollection.AddScoped<TInterface, TClass>(sp => sp.GetRequiredService<TClass>()); 
    } 
} 

Man könnte dies nennen wie so:

serviceCollection.AddScopedInterfaceAndClass<IMyInterface, MyClass>(); 

Ich schätze, dass AddScopedInterfaceAndClass nicht der perfekte Name ist - es ist nur ein Beispiel für die Idee zu demonstrieren. Außerdem gibt es immer noch den Nachteil, dass Sie sich merken müssen, um diese Erweiterung lieber als AddScoped zu verwenden.

Hinweis: Sie könnten die zweite AddScoped in der Verlängerung Verfahren vereinfachen, indem die zweite generische Entfernung (TClass), wie dies durch den Compiler abgeleitet wird.

+0

Ja, scheint es gibt keinen besseren Weg als das. Raten Sie, es ist der Preis, den Sie für diesen kleinen Legacy-Code zahlen müssen. Gibt Anlass zum Umschreiben. ;) –

Verwandte Themen