.NET Core开发日志——依赖注入
依赖注入(DI)不是一个新的话题,它的出现是伴随着系统解耦的需要而几乎必然产生的。
在SOLID设计原则中,DIP(Dependency inversion principle)——依赖倒置,规定了“需依赖抽象,而非实现”的准则,该原则主要目的是通过引入抽象(比如接口)的方式降低模块之间的耦合性。与此原则相拟而又有所不同的是IoC(inversion of control)——控制反转设计原则。这项原则定义了应该由通用框架而非外部代码决定控制流(control flow)的概念。对控制反转的实现有数种技术,DI(Dependency injection)——依赖注入便是其中之一,而依赖注入技术同时又支持依赖倒置的设计原则,所以它被广泛使用并不是件令人意外的事情。
依赖注入的基本特性是借由一个对象提供对另一对象的依赖。这样的一个对象通常又被称为容器。容器负责被依赖对象的注册(register),解析(resolve)与释放(release),并具有将被依赖对象注入到依赖对象内部的功能。
在之前的ASP.NET开发过程中,要想使用到依赖注入技术必需依赖第三方类库,而在ASP.NET Core中,这项技术已经被引入到其自身的框架中。
容器
ASP.NET Core中使用ServiceProvider作为依赖注入的容器,它是在WebHostBuilder类中被引入的。
public IWebHost Build()
{
...
IServiceProvider GetProviderFromFactory(IServiceCollection collection)
{
var provider = collection.BuildServiceProvider();
var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>();
if (factory != null)
{
using (provider)
{
return factory.CreateServiceProvider(factory.CreateBuilder(collection));
}
}
return provider;
}
}
注册
所需依赖的对象通过ServiceCollectionServiceExtensions中的各种扩展方法被加入到ServiceCollection类中。ServiceCollection类内部维护着一个ServiceDescriptor集合。而ServiceCollection又会被传入ServiceProvider的构造方法。
public static IServiceCollection AddTransient(
this IServiceCollection services,
Type serviceType,
Type implementationType)
{
...
return Add(services, serviceType, implementationType, ServiceLifetime.Transient);
}
public static IServiceCollection AddScoped(
this IServiceCollection services,
Type serviceType,
Type implementationType)
{
...
return Add(services, serviceType, implementationType, ServiceLifetime.Scoped);
}
public static IServiceCollection AddSingleton(
this IServiceCollection services,
Type serviceType,
Type implementationType)
{
...
return Add(services, serviceType, implementationType, ServiceLifetime.Singleton);
}
private static IServiceCollection Add(
IServiceCollection collection,
Type serviceType,
Type implementationType,
ServiceLifetime lifetime)
{
var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
collection.Add(descriptor);
return collection;
}
解析
要想获得已注册的对象,可以通过ServiceProviderServiceExtensions类的扩展方法GetService。
public static T GetService<T>(this IServiceProvider provider)
{
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}
return (T)provider.GetService(typeof(T));
}
ServiceProvider的GetService方法其实是调用了它内部各种引擎的父类ServiceProviderEngine的方法。这些引擎间区别在于实现方式以及性能上,功能方面都是一样的。默认引擎是DynamicServiceProviderEngine。
private readonly IServiceProviderEngine _engine;
internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
{
IServiceProviderEngineCallback callback = null;
if (options.ValidateScopes)
{
callback = this;
_callSiteValidator = new CallSiteValidator();
}
switch (options.Mode)
{
case ServiceProviderMode.Dynamic:
_engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
break;
case ServiceProviderMode.Runtime:
_engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
break;
#if IL_EMIT
case ServiceProviderMode.ILEmit:
_engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
break;
#endif
case ServiceProviderMode.Expressions:
_engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
break;
default:
throw new NotSupportedException(nameof(options.Mode));
}
}
public object GetService(Type serviceType) => _engine.GetService(serviceType);
ServiceProviderEngine类的CreateServiceAccessor方法创建了CallSite。
internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor);
_callback?.OnResolve(serviceType, serviceProviderEngineScope);
return realizedService.Invoke(serviceProviderEngineScope);
}
private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
{
var callSite = CallSiteFactory.CreateCallSite(serviceType, new CallSiteChain());
if (callSite != null)
{
_callback?.OnCreate(callSite);
return RealizeService(callSite);
}
return _ => null;
}
假设注册是用的public static IServiceCollection AddSingleton(this IServiceCollection services, Type serviceType, Type implementationType)
方法,那么之后的处理会生成一个SingletonCallSite对象并且包含ConstructorCallSite参数值。
private IServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain)
{
if (serviceType == descriptor.ServiceType)
{
IServiceCallSite callSite;
if (descriptor.ImplementationInstance != null)
{
callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.ImplementationInstance);
}
else if (descriptor.ImplementationFactory != null)
{
callSite = new FactoryCallSite(descriptor.ServiceType, descriptor.ImplementationFactory);
}
else if (descriptor.ImplementationType != null)
{
callSite = CreateConstructorCallSite(descriptor.ServiceType, descriptor.ImplementationType, callSiteChain);
}
else
{
throw new InvalidOperationException("Invalid service descriptor");
}
return ApplyLifetime(callSite, descriptor, descriptor.Lifetime);
}
return null;
}
private IServiceCallSite CreateConstructorCallSite(Type serviceType, Type implementationType, CallSiteChain callSiteChain)
{
callSiteChain.Add(serviceType, implementationType);
var constructors = implementationType.GetTypeInfo()
.DeclaredConstructors
.Where(constructor => constructor.IsPublic)
.ToArray();
IServiceCallSite[] parameterCallSites = null;
if (constructors.Length == 0)
{
throw new InvalidOperationException(Resources.FormatNoConstructorMatch(implementationType));
}
else if (constructors.Length == 1)
{
var constructor = constructors[0];
var parameters = constructor.GetParameters();
if (parameters.Length == 0)
{
return new CreateInstanceCallSite(serviceType, implementationType);
}
parameterCallSites = CreateArgumentCallSites(
serviceType,
implementationType,
callSiteChain,
parameters,
throwIfCallSiteNotFound: true);
return new ConstructorCallSite(serviceType, constructor, parameterCallSites);
}
...
}
private IServiceCallSite ApplyLifetime(IServiceCallSite serviceCallSite, object cacheKey, ServiceLifetime descriptorLifetime)
{
if (serviceCallSite is ConstantCallSite)
{
return serviceCallSite;
}
switch (descriptorLifetime)
{
case ServiceLifetime.Transient:
return new TransientCallSite(serviceCallSite);
case ServiceLifetime.Scoped:
return new ScopedCallSite(serviceCallSite, cacheKey);
case ServiceLifetime.Singleton:
return new SingletonCallSite(serviceCallSite, cacheKey);
default:
throw new ArgumentOutOfRangeException(nameof(descriptorLifetime));
}
}
ServiceProvider真正解析的是这个生成出来的CallSite对象。
protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
{
var callCount = 0;
return scope =>
{
if (Interlocked.Increment(ref callCount) == 2)
{
Task.Run(() => base.RealizeService(callSite));
}
return RuntimeResolver.Resolve(callSite, scope);
};
}
public object Resolve(IServiceCallSite callSite, ServiceProviderEngineScope scope)
{
return VisitCallSite(callSite, scope);
}
protected virtual TResult VisitCallSite(IServiceCallSite callSite, TArgument argument)
{
switch (callSite.Kind)
{
case CallSiteKind.Factory:
return VisitFactory((FactoryCallSite)callSite, argument);
case CallSiteKind.IEnumerable:
return VisitIEnumerable((IEnumerableCallSite)callSite, argument);
case CallSiteKind.Constructor:
return VisitConstructor((ConstructorCallSite)callSite, argument);
case CallSiteKind.Transient:
return VisitTransient((TransientCallSite)callSite, argument);
case CallSiteKind.Singleton:
return VisitSingleton((SingletonCallSite)callSite, argument);
case CallSiteKind.Scope:
return VisitScoped((ScopedCallSite)callSite, argument);
case CallSiteKind.Constant:
return VisitConstant((ConstantCallSite)callSite, argument);
case CallSiteKind.CreateInstance:
return VisitCreateInstance((CreateInstanceCallSite)callSite, argument);
case CallSiteKind.ServiceProvider:
return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);
case CallSiteKind.ServiceScopeFactory:
return VisitServiceScopeFactory((ServiceScopeFactoryCallSite)callSite, argument);
default:
throw new NotSupportedException($"Call site type {callSite.GetType()} is not supported");
}
}
因为上例中CallSite的类型是Constructor,所以最终通过VisitConstructor方法获得所依赖的对象。
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
{
object[] parameterValues = new object[constructorCallSite.ParameterCallSites.Length];
for (var index = 0; index < parameterValues.Length; index++)
{
parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], scope);
}
try
{
return constructorCallSite.ConstructorInfo.Invoke(parameterValues);
}
catch (Exception ex) when (ex.InnerException != null)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
// The above line will always throw, but the compiler requires we throw explicitly.
throw;
}
}
至于创建对象的方法是用反射,表达式树(Expression Tree)还是IL Emit,则取决于所使用的内部引擎。
释放
创建ServiceProviderEngine的时候会为其Root属性绑定ServiceProviderEngineScope类型的值,Root = new ServiceProviderEngineScope(this);
。
在ServiceProviderEngineScope类内部有着用于释放资源的Dispose方法。
public void Dispose()
{
lock (ResolvedServices)
{
if (_disposed)
{
return;
}
_disposed = true;
if (_disposables != null)
{
for (var i = _disposables.Count - 1; i >= 0; i--)
{
var disposable = _disposables[i];
disposable.Dispose();
}
_disposables.Clear();
}
ResolvedServices.Clear();
}
}
internal object CaptureDisposable(object service)
{
_captureDisposableCallback?.Invoke(service);
if (!ReferenceEquals(this, service))
{
if (service is IDisposable disposable)
{
lock (ResolvedServices)
{
if (_disposables == null)
{
_disposables = new List<IDisposable>();
}
_disposables.Add(disposable);
}
}
}
return service;
}
并不是所有对象都会通过ServiceProvider容器释放资源,只有容器自己创建的才可以。如果是新建对象再传入容器,容器不会为其作处理。
public void ConfigureServices(IServiceCollection services)
{
// 容器创建了实例所以会释放它。
services.AddScoped<Service1>();
services.AddSingleton<Service2>();
services.AddSingleton<ISomeService>(sp => new SomeServiceImplementation());
// 容器没有创建实例所以不会释放它。
services.AddSingleton<Service3>(new Service3());
services.AddSingleton(new Service3());
}
注入
ASP.NET Core中最常用的是Constructor Inject(构造器注入)方式。在其MVC框架中,通过DefaultControllerActivator生成Controller时,就可以跟踪到依赖注入是如何被其使用的。
public virtual object Create(ControllerContext controllerContext)
{
...
var serviceProvider = controllerContext.HttpContext.RequestServices;
return _typeActivatorCache.CreateInstance<object>(serviceProvider, controllerTypeInfo.AsType());
}
DefaultControllerActivator的Create方法使用了TypeActivatorCache类,其内部用到了ActivatorUtilities.CreateFactory方法。
public class TypeActivatorCache : ITypeActivatorCache
{
private readonly Func<Type, ObjectFactory> _createFactory =
(type) => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);
private readonly ConcurrentDictionary<Type, ObjectFactory> _typeActivatorCache =
new ConcurrentDictionary<Type, ObjectFactory>();
public TInstance CreateInstance<TInstance>(
IServiceProvider serviceProvider,
Type implementationType)
{
...
var createFactory = _typeActivatorCache.GetOrAdd(implementationType, _createFactory);
return (TInstance)createFactory(serviceProvider, arguments: null);
}
}
ActivatorUtilities类位于ServiceProvider同样的程序集中。
public static ObjectFactory CreateFactory(Type instanceType, Type[] argumentTypes)
{
FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap);
var provider = Expression.Parameter(typeof(IServiceProvider), "provider");
var argumentArray = Expression.Parameter(typeof(object[]), "argumentArray");
var factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);
var factoryLamda = Expression.Lambda<Func<IServiceProvider, object[], object>>(
factoryExpressionBody, provider, argumentArray);
var result = factoryLamda.Compile();
return result.Invoke;
}
留意BuildFactoryExpression方法中GetServiceInfo变量。
private static Expression BuildFactoryExpression(
ConstructorInfo constructor,
int?[] parameterMap,
Expression serviceProvider,
Expression factoryArgumentArray)
{
var constructorParameters = constructor.GetParameters();
var constructorArguments = new Expression[constructorParameters.Length];
for (var i = 0; i < constructorParameters.Length; i++)
{
var constructorParameter = constructorParameters[i];
var parameterType = constructorParameter.ParameterType;
var hasDefaultValue = ParameterDefaultValue.TryGetDefaultValue(constructorParameter, out var defaultValue);
if (parameterMap[i] != null)
{
constructorArguments[i] = Expression.ArrayAccess(factoryArgumentArray, Expression.Constant(parameterMap[i]));
}
else
{
var parameterTypeExpression = new Expression[] { serviceProvider,
Expression.Constant(parameterType, typeof(Type)),
Expression.Constant(constructor.DeclaringType, typeof(Type)),
Expression.Constant(hasDefaultValue) };
constructorArguments[i] = Expression.Call(GetServiceInfo, parameterTypeExpression);
}
...
}
return Expression.New(constructor, constructorArguments);
}
GetServiceInfo变量申明了对GetService方法的调用,而此GetService其实正是对ServiceProvider的调用。
private static readonly MethodInfo GetServiceInfo =
GetMethodInfo<Func<IServiceProvider, Type, Type, bool, object>>((sp, t, r, c) => GetService(sp, t, r, c));
private static object GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
{
var service = sp.GetService(type);
...
return service;
}
通过以上的处理,在创建Controller时,其构造方法中所需参数的类型也会被容器解析,创建相应实例,从而实现依赖注入功能。
生命周期
ASP.NET Core容器可以创建三种生命周期的对象:
- Transient, 每次取得的都是新的对象。
- Scoped, 每次ASP.NET请求生成不同的对象。
- Singleton,同一对象只会生成一次。
检视这三者类型的构造方法,可以很容易理解Scoped与Singleton是通过缓存的方式实现对象的重用。
public TransientCallSite(IServiceCallSite serviceCallSite)
{
ServiceCallSite = serviceCallSite;
}
public ScopedCallSite(IServiceCallSite serviceCallSite, object cacheKey)
{
ServiceCallSite = serviceCallSite;
CacheKey = cacheKey;
}
public SingletonCallSite(IServiceCallSite serviceCallSite, object cacheKey) : base(serviceCallSite, cacheKey)
{
}
性能
由于ServiceProvider容器使用了反射,表达式树以及IL Emit方式创建对象,可能会对其性能有所担忧,但实际检测的结果,除了Runtime引擎表现不尽如人意外,其它引擎的性能还是在可接受范围内的。
public class GetServiceBenchmark
{
private const int OperationsPerInvoke = 50000;
private IServiceProvider _transientSp;
private ServiceProviderMode _mode;
[Params("Expressions", "Dynamic", "Runtime", "ILEmit")]
public string Mode {
set {
_mode = (ServiceProviderMode)Enum.Parse(typeof(ServiceProviderMode), value);
}
}
[Benchmark(Baseline = true, OperationsPerInvoke = OperationsPerInvoke)]
public void NoDI()
{
for (int i = 0; i < OperationsPerInvoke; i++)
{
var temp = new A(new B(new C()));
temp.Foo();
}
}
[GlobalSetup(Target = nameof(Transient))]
public void SetupTransient()
{
var services = new ServiceCollection();
services.AddTransient<A>();
services.AddTransient<B>();
services.AddTransient<C>();
_transientSp = services.BuildServiceProvider(new ServiceProviderOptions()
{
Mode = _mode
});
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Transient()
{
for (int i = 0; i < OperationsPerInvoke; i++)
{
var temp = _transientSp.GetService<A>();
temp.Foo();
}
}
private class A
{
public A(B b)
{
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void Foo()
{
}
}
private class B
{
public B(C c)
{
}
}
private class C
{
}
}
// ***** BenchmarkRunner: Finish *****
// * Export *
// * Detailed results *
GetServiceBenchmark.NoDI: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Dynamic]
Runtime = ; GC =
Mean = 5.5175 ns, StdErr = 0.0116 ns (0.21%); N = 15, StdDev = 0.0449 ns
Min = 5.4490 ns, Q1 = 5.4860 ns, Median = 5.5207 ns, Q3 = 5.5641 ns, Max = 5.5972 ns
IQR = 0.0781 ns, LowerFence = 5.3688 ns, UpperFence = 5.6813 ns
ConfidenceInterval = [5.4695 ns; 5.5654 ns] (CI 99.9%), Margin = 0.0480 ns (0.87% of Mean)
Skewness = 0.15, Kurtosis = 1.67
GetServiceBenchmark.Transient: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Dynamic]
Runtime = ; GC =
Mean = 43.1601 ns, StdErr = 0.0677 ns (0.16%); N = 15, StdDev = 0.2620 ns
Min = 42.7731 ns, Q1 = 42.9117 ns, Median = 43.2403 ns, Q3 = 43.3580 ns, Max = 43.5392 ns
IQR = 0.4464 ns, LowerFence = 42.2421 ns, UpperFence = 44.0276 ns
ConfidenceInterval = [42.8800 ns; 43.4402 ns] (CI 99.9%), Margin = 0.2801 ns (0.65% of Mean)
Skewness = -0.2, Kurtosis = 1.59
GetServiceBenchmark.NoDI: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Expressions]
Runtime = ; GC =
Mean = 5.6964 ns, StdErr = 0.0388 ns (0.68%); N = 33, StdDev = 0.2226 ns
Min = 5.5148 ns, Q1 = 5.5603 ns, Median = 5.6042 ns, Q3 = 5.6769 ns, Max = 6.2460 ns
IQR = 0.1166 ns, LowerFence = 5.3854 ns, UpperFence = 5.8518 ns
ConfidenceInterval = [5.5561 ns; 5.8368 ns] (CI 99.9%), Margin = 0.1404 ns (2.46% of Mean)
Skewness = 1.48, Kurtosis = 3.69
GetServiceBenchmark.Transient: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Expressions]
Runtime = ; GC =
Mean = 43.6662 ns, StdErr = 0.0995 ns (0.23%); N = 13, StdDev = 0.3586 ns
Min = 43.1083 ns, Q1 = 43.5089 ns, Median = 43.6051 ns, Q3 = 43.7178 ns, Max = 44.6669 ns
IQR = 0.2089 ns, LowerFence = 43.1956 ns, UpperFence = 44.0311 ns
ConfidenceInterval = [43.2368 ns; 44.0957 ns] (CI 99.9%), Margin = 0.4295 ns (0.98% of Mean)
Skewness = 1.41, Kurtosis = 5.18
GetServiceBenchmark.NoDI: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=ILEmit]
Runtime = ; GC =
Mean = 5.6016 ns, StdErr = 0.0071 ns (0.13%); N = 13, StdDev = 0.0255 ns
Min = 5.5547 ns, Q1 = 5.5896 ns, Median = 5.5996 ns, Q3 = 5.6226 ns, Max = 5.6400 ns
IQR = 0.0330 ns, LowerFence = 5.5401 ns, UpperFence = 5.6721 ns
ConfidenceInterval = [5.5712 ns; 5.6321 ns] (CI 99.9%), Margin = 0.0305 ns (0.54% of Mean)
Skewness = -0.47, Kurtosis = 2.12
GetServiceBenchmark.Transient: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=ILEmit]
Runtime = ; GC =
Mean = 43.1397 ns, StdErr = 0.0726 ns (0.17%); N = 15, StdDev = 0.2812 ns
Min = 42.7061 ns, Q1 = 42.9064 ns, Median = 43.1052 ns, Q3 = 43.3093 ns, Max = 43.6443 ns
IQR = 0.4028 ns, LowerFence = 42.3022 ns, UpperFence = 43.9135 ns
ConfidenceInterval = [42.8392 ns; 43.4403 ns] (CI 99.9%), Margin = 0.3006 ns (0.70% of Mean)
Skewness = 0.28, Kurtosis = 1.9
GetServiceBenchmark.NoDI: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Runtime]
Runtime = ; GC =
Mean = 6.4814 ns, StdErr = 0.0762 ns (1.18%); N = 100, StdDev = 0.7617 ns
Min = 5.4979 ns, Q1 = 5.8327 ns, Median = 6.3039 ns, Q3 = 6.9775 ns, Max = 8.0420 ns
IQR = 1.1448 ns, LowerFence = 4.1155 ns, UpperFence = 8.6947 ns
ConfidenceInterval = [6.2231 ns; 6.7397 ns] (CI 99.9%), Margin = 0.2583 ns (3.99% of Mean)
Skewness = 0.52, Kurtosis = 1.94
GetServiceBenchmark.Transient: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Runtime]
Runtime = ; GC =
Mean = 581.5066 ns, StdErr = 1.6962 ns (0.29%); N = 15, StdDev = 6.5695 ns
Min = 571.4934 ns, Q1 = 576.3829 ns, Median = 580.8121 ns, Q3 = 587.2645 ns, Max = 596.3317 ns
IQR = 10.8816 ns, LowerFence = 560.0605 ns, UpperFence = 603.5869 ns
ConfidenceInterval = [574.4834 ns; 588.5297 ns] (CI 99.9%), Margin = 7.0232 ns (1.21% of Mean)
Skewness = 0.58, Kurtosis = 2.48
Total time: 00:03:11 (191.85 sec)
第三方容器
如果想用第三方容器替换ASP.NET Core原有的容器也是可以办到的。以最常见的Autofac为例,有两种实现方式:
借助ConfigureContainer方法,要先在Program类中挂载AddAutofac方法。
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args).ConfigureServices(services => services.AddAutofac())
.UseStartup<Startup>();
}
然后在Startup类中加入ConfigureContainer方法。
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new DefaultModule());
}
例中的DefaultModule类按照Autofac的通用方式实现。
public class DefaultModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<HelloWorld>().As<IHelloWorld>();
}
}
如果不想使用ConfigureContainer方法,也可以直接利用ConfigureServices方法:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<DefaultModule>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
需要注意的是,使用这种方式时,ConfigureServices方法的返回类型要从void改成IServiceProvider。
.NET Core开发日志——依赖注入的更多相关文章
- C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志
C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...
- ASP.NET Core中的依赖注入(1):控制反转(IoC)
ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...
- NET Core开发-获取所有注入(DI)服务
NET Core开发-获取所有注入(DI)服务 获取ASP.NET Core中所有注入(DI)服务,在ASP.NET Core中加入了Dependency Injection依赖注入. 我们在Cont ...
- 深入理解net core中的依赖注入、Singleton、Scoped、Transient(一)
相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient ...
- ASPNETCOREAPI 跨域处理 SQL 语句拼接 多条件分页查询 ASPNET CORE 核心 通过依赖注入(注入服务)
ASPNETCOREAPI 跨域处理 AspNetCoreApi 跨域处理 如果咱们有处理过MV5 跨域问题这个问题也不大. (1)为什么会出现跨域问题: 浏览器安全限制了前端脚本跨站点的访问资源, ...
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
- ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...
- ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】
本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...
随机推荐
- dyld`__abort_with_payload:
dyld`__abort_with_payload: 0x1030422f0 <+0>: mov x16, #0x209 0x1030422f4 <+4>: svc ...
- Android——RatingBar(评价条)相关知识总结贴
android用户界面之RatingBar教程实例汇总 http://www.apkbus.com/android-51346-1-1.html Android 中文 API (40) —— Rati ...
- 动态改变APP图标
一.iOS动态更换App图标(一):基础使用 该功能应用的场景 1.白天/夜间模式切换,在切换App主色调同时切换App图标. 2.各类皮肤主题(淘宝就可换肤),附带App图标一块更换. 3.利用Ap ...
- Wifiner for Mac(WiFi 状况分析工具)破解版安装
1.软件简介 Wifiner 是 macOS 系统上一款 Wifi 分析工具,仅需几次点击即可对您的 Wi-Fi 网络连接进行分析和故障排除.扫描您的 Wi-Fi 网络,获取包含交互式彩色编码热 ...
- 浏览器URL参数解决方案
function getUrlParams() { var search = window.location.search; // 写入数据字典 , search.length).split(&quo ...
- struts2:表单标签
目录 表单标签1. form标签2. submit标签3. checkbox标签4. checkboxlist标签5. combobox标签6. doubleselect标签7. head标签8. f ...
- 图解ByteBuffer
https://www.cnblogs.com/ruber/p/6857159.html https://www.e-learn.cn/content/qita/750752 https://blog ...
- linux每日命令(21):find命令之exec
find是我们很常用的一个Linux命令,但是我们一般查找出来的并不仅仅是看看而已,还会有进一步的操作,这个时候exec的作用就显现出来了. 一. exec参数说明: -exec 参数后面跟的是com ...
- 1. RNN神经网络模型原理
1. RNN神经网络模型原理 2. RNN神经网络模型的不同结构 3. RNN神经网络-LSTM模型结构 1. 前言 循环神经网络(recurrent neural network)源自于1982年由 ...
- rsync安装及部署
一.服务器端1.yum -y install rsync xinetd 2.vi /etc/xinetd.d/rsync 将yes 修改为no IPV6修改为IPV4 3.vi /etc/rsyncd ...