C# .NET Core DI,为包注册默认实现
如何使用.NET Core的IoC容器注册默认实现,并提供覆盖现有实现的方法 例如,我可能想创建一个为某些服务提供默认实现的包C# .NET Core DI,为包注册默认实现,c#,asp.net-core,dependency-injection,.net-core,inversion-of-control,C#,Asp.net Core,Dependency Injection,.net Core,Inversion Of Control,如何使用.NET Core的IoC容器注册默认实现,并提供覆盖现有实现的方法 例如,我可能想创建一个为某些服务提供默认实现的包 namesapce Package { public interface ISomeService { } public class Default : ISomeService { } } 然后在同一个包中使用此服务 namesapce Package { public class Service { Serv
namesapce Package
{
public interface ISomeService { }
public class Default : ISomeService { }
}
然后在同一个包中使用此服务
namesapce Package
{
public class Service
{
Service(ISomeService service) { }
}
}
如何注册ISomeService
默认实现
稍后在某些项目中使用此包并希望用另一个替代现有实现时,默认值应替换为替代
namespace Project
{
public class Override : ISomeService { }
}
内置的.NET Core DI容器允许应用程序开发人员通过将相同的服务附加到
ServiceCollection
来覆盖包对ServiceCollection
的注册。如果对单个服务类型进行了多次注册,则将使用最后一次注册。例如:
//包注册(包的一部分)
services.AddTransient();
//由应用程序开发人员覆盖(属于Startup.cs的一部分)
services.AddTransient();
一定要考虑以不需要使用DI容器的方式构建包,正如Mark Seemann在其文章中所描述的。内置的.NET Core DI容器允许应用程序开发人员通过简单地将相同的服务附加到
ServiceCollection
来覆盖包对ServiceCollection
的注册。如果对单个服务类型进行了多次注册,则将使用最后一次注册。例如:
//包注册(包的一部分)
services.AddTransient();
//由应用程序开发人员覆盖(属于Startup.cs的一部分)
services.AddTransient();
一定要考虑以不需要使用DI容器的方式构建包,如Mark Seemann在其文章中所述。如果包中包含配置
IServiceCollection
的类,例如:
public class MyPackageInstaller
{
public void Install(IServiceCollection services)
{
// Your package registers its services
}
}
然后,您也可以允许消费者进行可选更改。例如,您可以定义这样一个类,它允许使用者指定特定服务的实现:
public class MyPackageRegistrationOptions
{
public ServiceDescriptor FooServiceDescriptor { get; private set; }
public void AddFooService(ServiceDescriptor fooDescriptor)
{
if (fooDescriptor.ServiceType != typeof(IFooService))
{
throw new ArgumentException("fooDescriptor must register type IFooService.");
}
FooServiceDescriptor = fooDescriptor;
}
}
现在,您的安装程序可以选择这些选项,并注册使用者指定的实现或它自己的默认实现
public class MyPackageInstaller
{
private readonly MyPackageRegistrationOptions _options;
public MyPackageInstaller(MyPackageRegistrationOptions options = null)
{
_options = options;
}
public void Install(IServiceCollection services)
{
if (_options?.FooServiceDescriptor != null)
services.Add(_options.FooServiceDescriptor);
else
// here's your default implementation
services.AddSingleton<FooService>();
}
}
公共类MyPackageInstaller
{
私有只读MyPackageRegistrationOptions\u选项;
公共MyPackageInstaller(MyPackageRegistrationOptions=null)
{
_选项=选项;
}
public void安装(IServiceCollection服务)
{
if(_options?.FooServiceDescriptor!=null)
添加(_options.FooServiceDescriptor);
其他的
//这是您的默认实现
services.AddSingleton();
}
}
用法:
var services = new ServiceCollection();
var options = new MyPackageRegistrationOptions();
options.AddFooService(ServiceDescriptor.Singleton<IFooService, AlternateFooService>());
var installer = new MyPackageInstaller(options);
installer.Install(services);
var services=newservicecolection();
var options=新的MyPackageRegistrationOptions();
options.AddFooService(ServiceDescriptor.Singleton());
var安装程序=新的MyPackageInstaller(选项);
安装程序。安装(服务);
乍一看,要获得同样的结果,这似乎是一条漫长的道路。好处是它允许您更清楚地说明哪些服务应该或不应该被覆盖。这样,您感觉更像是在使用故意公开的配置选项,而不是戳包的内部
namesapce Package
{
public class Service
{
Service(ISomeService service) { }
}
}
您可以不允许使用者添加服务描述符
,而只允许他们指定服务类型,并且您的配置决定如何注册服务(单例、瞬态等)
当库依赖于用户必须提供的连接字符串等配置值时,这也是一种有用的模式。您可以将这些选项设置为构造选项所必需的参数,然后再将这些选项设置为构造安装程序所必需的参数,或者在安装程序中仅将它们设置为必需的参数。现在,如果没有所需的配置值,就无法安装该软件包。如果您的软件包包含配置IServiceCollection的类,例如:
public class MyPackageInstaller
{
public void Install(IServiceCollection services)
{
// Your package registers its services
}
}
然后,您也可以允许消费者进行可选更改。例如,您可以定义这样一个类,它允许使用者指定特定服务的实现:
public class MyPackageRegistrationOptions
{
public ServiceDescriptor FooServiceDescriptor { get; private set; }
public void AddFooService(ServiceDescriptor fooDescriptor)
{
if (fooDescriptor.ServiceType != typeof(IFooService))
{
throw new ArgumentException("fooDescriptor must register type IFooService.");
}
FooServiceDescriptor = fooDescriptor;
}
}
现在,您的安装程序可以选择这些选项,并注册使用者指定的实现或它自己的默认实现
public class MyPackageInstaller
{
private readonly MyPackageRegistrationOptions _options;
public MyPackageInstaller(MyPackageRegistrationOptions options = null)
{
_options = options;
}
public void Install(IServiceCollection services)
{
if (_options?.FooServiceDescriptor != null)
services.Add(_options.FooServiceDescriptor);
else
// here's your default implementation
services.AddSingleton<FooService>();
}
}
公共类MyPackageInstaller
{
私有只读MyPackageRegistrationOptions\u选项;
公共MyPackageInstaller(MyPackageRegistrationOptions=null)
{
_选项=选项;
}
public void安装(IServiceCollection服务)
{
if(_options?.FooServiceDescriptor!=null)
添加(_options.FooServiceDescriptor);
其他的
//这是您的默认实现
services.AddSingleton();
}
}
用法:
var services = new ServiceCollection();
var options = new MyPackageRegistrationOptions();
options.AddFooService(ServiceDescriptor.Singleton<IFooService, AlternateFooService>());
var installer = new MyPackageInstaller(options);
installer.Install(services);
var services=newservicecolection();
var options=新的MyPackageRegistrationOptions();
options.AddFooService(ServiceDescriptor.Singleton());
var安装程序=新的MyPackageInstaller(选项);
安装程序。安装(服务);
乍一看,要获得同样的结果,这似乎是一条漫长的道路。好处是它允许您更清楚地说明哪些服务应该或不应该被覆盖。这样,您感觉更像是在使用故意公开的配置选项,而不是戳包的内部
namesapce Package
{
public class Service
{
Service(ISomeService service) { }
}
}
您可以不允许使用者添加服务描述符
,而只允许他们指定服务类型,并且您的配置决定如何注册服务(单例、瞬态等)
当库依赖于诸如连接字符串之类的配置值时,这也是一种有用的模式