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

时间:2017-12-06 15:10:06

标签: c# dependency-injection asp.net-core

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

我尝试做的第一件事是:

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

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

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

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

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

即。作为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 { }
}

1 个答案:

答案 0 :(得分:5)

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

class PropertyDetails extends React.Component {
  static propTypes = {
    breadcrumbChange: PropTypes.func,
    folderId: PropTypes.string.isRequired
  }

  /**
   * @typedef {Object} Props
   * @property {function} breadcrumbChange
   * @property {string} folderId
   */

  /**
   * @constructor
   * @param {Props} props
   */
  constructor(props) {
    super(props);

您可以这样称呼:

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

我很欣赏serviceCollection.AddScopedInterfaceAndClass<IMyInterface, MyClass>(); 并不是一个完美的名字 - 这只是展示这个想法的一个例子。此外,您还必须记住使用此扩展程序,而不是AddScopedInterfaceAndClass

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