ASP.NET Core DI:如果将作用域服务注册为服务类型和实现类型,则解析相同的实例

Nic*_*ler 3 c# dependency-injection asp.net-core

假设我有一个我想用scoped生命周期来解决的服务.但有时我尝试将其解析为接口类型,有时在实现类型.

我试图做的第一件事是:

ServiceCollection services;
services.AddScoped<MyClass>();
services.AddScoped<IMyInterface, MyClass>();
Run Code Online (Sandbox Code Playgroud)

上面的示例的问题是,如果我解析IMyInterface,则使用不同的实例,而不是解析MyClass.基本上,两个范围的实例可能同时存在.

我通过以下方式解决此问题.但它很容易出错,因为你很容易忘记在一个地方做这件事,而且很难注意到.

serviceCollection.AddScoped<MyClass>();
serviceCollection.AddScoped<IMyInterface, MyClass>(sp => sp.GetRequiredService<MyClass>());
Run Code Online (Sandbox Code Playgroud)

有没有办法以一种不易出错的方式完成我想要的东西.优先,但不一定,在一次注册?

即作为xUnit测试:

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 { }
}
Run Code Online (Sandbox Code Playgroud)

Kir*_*kin 5

一种选择是创建自己的扩展方法,将您在问题中显示的两条线包装起来.例如:

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>());
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以这样称呼它:

serviceCollection.AddScopedInterfaceAndClass<IMyInterface, MyClass>();
Run Code Online (Sandbox Code Playgroud)

我很欣赏这AddScopedInterfaceAndClass不是一个完美的名字 - 这只是一个展示这个想法的例子.此外,还有一个缺点,你必须记住使用此扩展而不是AddScoped.

注意:您可以AddScoped通过删除第二个generic(TClass)来简化扩展方法中的第二个,因为这是由编译器推断的.