C# Net核心:使用多个接口和生活方式单例注册实现

C# Net核心:使用多个接口和生活方式单例注册实现,c#,dependency-injection,asp.net-core,inversion-of-control,C#,Dependency Injection,Asp.net Core,Inversion Of Control,考虑以下接口和类定义: public interface IInterface1 { } public interface IInterface2 { } public class MyClass : IInterface1, IInterface2 { } 是否有任何方法可以使用以下多个接口注册一个MyClass实例: ... services.AddSingleton<IInterface1, IInterface2, MyClass>(); ... IInterface1

考虑以下接口和类定义:

public interface IInterface1 { }
public interface IInterface2 { }
public class MyClass : IInterface1, IInterface2 { }
是否有任何方法可以使用以下多个接口注册一个
MyClass
实例:

...
services.AddSingleton<IInterface1, IInterface2, MyClass>();
...
IInterface1 interface1 = app.ApplicationServices.GetService<IInterface1>();
IInterface2 interface2 = app.ApplicationServices.GetService<IInterface2>();

根据定义,服务集合是
ServiceDescriptor
s的集合,它们是成对的服务类型和实现类型

但是,您可以通过创建自己的提供程序函数来解决这个问题,类似这样的函数(感谢user7224827):

services.AddSingleton();
services.AddSingleton(x=>x.GetService());
更多选项如下:

private static MyClass ClassInstance;

public void ConfigureServices(IServiceCollection services)
{
    ClassInstance = new MyClass();
    services.AddSingleton<IInterface1>(provider => ClassInstance);
    services.AddSingleton<IInterface2>(provider => ClassInstance);
}
私有静态MyClass ClassInstance;
public void配置服务(IServiceCollection服务)
{
ClassInstance=新的MyClass();
services.AddSingleton(provider=>ClassInstance);
services.AddSingleton(provider=>ClassInstance);
}
另一种方法是:

public void ConfigureServices(IServiceCollection services)
{
    ClassInstance = new MyClass();
    services.AddSingleton<IInterface1>(ClassInstance);
    services.AddSingleton<IInterface2>(ClassInstance);
}
public void配置服务(IServiceCollection服务)
{
ClassInstance=新的MyClass();
服务。AddSingleton(ClassInstance);
服务。AddSingleton(ClassInstance);
}

这里我们只提供相同的实例。

您可以包装user7224827的答案,创建一个与原始所需API匹配的良好扩展方法:

公共静态类serviceCollectionText
{
公共静态void AddSingleton(此IServiceCollection服务)
其中T:class,I1,I2
其中I1:类
其中I2:类
{
services.AddSingleton();
services.AddSingleton(x=>(T)x.GetService());
}
}

保持DI机制的另一个选项是执行以下操作:

services.AddSingleton<MyClass>();
services.AddSingleton<Interface1>(p => p.GetRequiredService<MyClass>());
services.AddSingleton<Interface2>(x => x.GetRequiredService<MyClass>());
services.AddSingleton();
services.AddSingleton(p=>p.GetRequiredService());
services.AddSingleton(x=>x.GetRequiredService());

以上答案很酷,我将其作为灵感加以修改,以利用框架附带的类型约束,避免了在使用不兼容的类和接口时出现强制转换和最有用的编译器错误。编译器错误比运行时的“what the f**why is this null”更容易解决;)

[TestClass()]
公共类ServiceCollectionExtensionTests
{
接口MyInterface
{
Guid Id{get;}
}
类MyClas:MyInterface
{
Guid id=Guid.NewGuid();
公共Guid Id=>Id;
}
[TestMethod()]
public void AddSingletonTest()
{
var service=newservicecolection()
.AddSingleton()
.ReUseSingleton()
.BuildServiceProvider();
var foo1=service.GetService();
var foo2=service.GetService();
AreEqual(foo1.Id,foo2.Id);
Assert.AreName(foo1,foo2);
}
}
“ReUseXYZ”的代码如下:

名称空间Microsoft.Extensions.DependencyInjection
{
/// 
///类ServiceCollectionExtension允许注册
///已注册服务的派生实现
///重复使用相同的服务而不必注册
///相同的类2x以2个
///同一范围内的同一类型。
/// 
公共静态类ServiceCollectionExtension
{
/// 
///将TBase中指定类型的单例服务与基于已在实现工厂中指定的注册类型T的工厂添加到指定的。
/// 
///注册类型
///T派生自的类型可以是基类或基接口。
///服务。
///用于向注册接口的IServiceCollection。
公共静态IServiceCollection ReUseSingleton(此IServiceCollection服务)
其中T:TBase
在哪里?基地:班级
{
services.AddSingleton(a=>a.GetRequiredService());
返回服务;
}
/// 
///将TBase中指定类型的临时服务与基于已在实现工厂中指定的注册类型T的工厂添加到指定的。
/// 
///注册类型
///T派生自的类型可以是基类或基接口。
///要扩展的IServiceCollection实例。
///服务。
///用于向注册接口的IServiceCollection。
公共静态IServiceCollection重用Transient(此IServiceCollection服务)
其中T:TBase
在哪里?基地:班级
{
services.AddTransient(a=>a.GetRequiredService());
返回服务;
}
/// 
///将TBase中指定类型的作用域服务与基于已在实现工厂中指定的注册类型T的工厂添加到指定的。
/// 
///注册类型
///T派生自的类型可以是基类或基接口。
///要扩展的IServiceCollection实例。
///服务。
///用于向注册接口的IServiceCollection。
公共静态IServiceCollection重用范围(此IServiceCollection服务)
其中T:TBase
在哪里?基地:班级
{
services.addScope(a=>a.GetRequiredService());
返回服务;
}
}
}

第三种方法是:
services.AddSingleton()
services.AddSingleton(x=>x.GetService())IInterface1
继承了
IInterface2
,那么就不需要自己创建类了。只有当
IInterface1
继承了
IInterface2
时,user7224827的解决方案才起作用,在这种情况下,绑定两者都很简单。要解决最初的问题,您需要像
services.AddSingleton()这样的东西;services.AddSingleton(s=>s.GetService());services.AddSingleto
services.AddSingleton<MyClass>();
services.AddSingleton<Interface1>(p => p.GetRequiredService<MyClass>());
services.AddSingleton<Interface2>(x => x.GetRequiredService<MyClass>());
[TestClass()]
public class ServiceCollectionExtensionTests
{
    interface MyInterface
    {
        Guid Id { get; }
    }
    class MyClas : MyInterface
    {
        Guid id = Guid.NewGuid();

        public Guid Id => id;

    }


    [TestMethod()]
    public void AddSingletonTest()
    {
        var service = new ServiceCollection()
                            .AddSingleton<MyClas>()
                            .ReUseSingleton<MyClas,MyInterface>()
                            .BuildServiceProvider();

        var foo1 = service.GetService<MyClas>();
        var foo2 = service.GetService<MyInterface>();
        Assert.AreEqual(foo1.Id, foo2.Id);
        Assert.AreSame(foo1, foo2);
    }
}
namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// Class ServiceCollectionExtension allowing to registered 
    /// derived implementations of a already registered service 
    /// to re-use the same service without having to register 
    /// the same class 2x ending up with 2 instances of the 
    /// same type in the same scope.
    /// </summary>
    public static class ServiceCollectionExtension
    {

        /// <summary>
        /// Adds a singleton service of the type specified in TBase with a factory based on the registered type T that has been specified in implementation factory to the specified <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection"/>.
        /// </summary>
        /// <typeparam name="T">The registered type</typeparam>
        /// <typeparam name="TBase">The type that T is derived from, can be the base class or base interface.</typeparam>
        /// <param name="services">The services.</param>
        /// <returns>the IServiceCollection used to register the interface with.</returns>
        public static IServiceCollection ReUseSingleton<T, TBase>(this IServiceCollection services)
            where T : TBase
            where TBase : class
        {
            services.AddSingleton<TBase>(a => a.GetRequiredService<T>());
            return services;
        }

        /// <summary>
        /// Adds a transient service of the type specified in TBase with a factory based on the registered type T that has been specified in implementation factory to the specified <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection"/>.
        /// </summary>
        /// <typeparam name="T">The registered type</typeparam>
        /// <typeparam name="TBase">The type that T is derived from, can be the base class or base interface.</typeparam>
        /// <typeparam name="TS">The IServiceCollection instance to extend.</typeparam>
        /// <param name="services">The services.</param>
        /// <returns>the IServiceCollection used to register the interface with.</returns>
        public static IServiceCollection ReUseTransient<T, TBase>(this IServiceCollection services)
            where T : TBase
            where TBase : class

        {
            services.AddTransient<TBase>(a => a.GetRequiredService<T>());
            return services;
        }

        /// <summary>
        /// Adds a scoped service of the type specified in TBase with a factory based on the registered type T that has been specified in implementation factory to the specified <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection"/>.
        /// </summary>
        /// <typeparam name="T">The registered type</typeparam>
        /// <typeparam name="TBase">The type that T is derived from, can be the base class or base interface.</typeparam>
        /// <typeparam name="TS">The IServiceCollection instance to extend.</typeparam>
        /// <param name="services">The services.</param>
        /// <returns>the IServiceCollection used to register the interface with.</returns>
        public static IServiceCollection ReUseScoped<T, TBase>(this IServiceCollection services)
            where T : TBase
            where TBase : class
        {
            services.AddScoped<TBase>(a => a.GetRequiredService<T>());
            return services;
        }
    }
}