目录
概念
AspnetCore中的依赖注入。
使用第三方依赖注入
关于依赖注入的概念,网上已经有很多解释,这里不多复述。简单讲有点类似计划经济下的物资分配,啥都先进仓库,然后按需分配。下边给几个链接,以供参考:
Architectural principles | Microsoft Docs
https://docs.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/architectural-principles#dependency-inversion
Angular - Angular 中的依赖注入
https://angular.cn/guide/dependency-injection
Core Technologieshttps://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans
话说,AspNetCore中的很多东西的初始化都比较复杂,比如创建Identity中的UserManager对象,我们看下构造函数:
public UserManager(IUserStore store, IOptions optionsAccessor, IPasswordHasher passwordHasher, IEnumerable> userValidators, IEnumerable> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger> logger);
这么多的参数,眼睛看花了没有?如果每个调用地方都new一下,会不会很崩溃?其实类似的东西还有很多,这可能就是为什么微软默认要在AspNetCore中加入依赖注入的原因吧!所以呢,这个依赖注入也很简单,主要实现了生命周期管理,及构造函数的注入方式。
因为实现简单,那用起来也简单。AspNetCore中,啥都放在StartUp.cs中,而且第三方组件提供商,一般都会写个IServiceCollection的扩展方法,并且在提供扩展方法中传递组件初始化所需的参数。
Log4net的初始化方法,里边生成了一个Log4NetProvider的实例。
public static ILoggerFactory AddLog4Net(this ILoggerFactory factory, Log4NetProviderOptions options)
{
factory.AddProvider(new Log4NetProvider(options));
return factory;
}
对于简单的对象,比如自己写的个class,可以直接调用相应的Add方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped();
services.AddScoped();
}
用的时候,就直接在构造函数里加上就是了:
public class IndexModel : PageModel
{
private readonly ILogger _logger;
private readonly IOperationTransient _transientOperation;
private readonly IOperationSingleton _singletonOperation;
private readonly IOperationScoped _scopedOperation;
public IndexModel(ILogger logger,
IOperationTransient transientOperation,
IOperationScoped scopedOperation,
IOperationSingleton singletonOperation)
{
_logger = logger;
_transientOperation = transientOperation;
_scopedOperation = scopedOperation;
_singletonOperation = singletonOperation;
}
public void OnGet()
{
_logger.LogInformation("Transient: " + _transientOperation.OperationId);
_logger.LogInformation("Scoped: " + _scopedOperation.OperationId);
_logger.LogInformation("Singleton: " + _singletonOperation.OperationId);
}
}
不过,如果依赖够多话,相应的构造函数也要写得非常的冗长了,真是佩服了AspNetCore的这些设计者们的脑回路,可能是为了活跃开源社区,故意留下这么个坑,让别人来填充。好在,对于Web开发中留了一个HttpContext.RequestServices ,可以直接获取相应的对象,这还算有点良心。比如可以这样:
public abstract class BaseController : Controller
{
protected ILogger Logger => HttpContext.RequestServices.GetService();
public UserManager UserManager => HttpContext.RequestServices.GetService>();
public RoleManager RoleManager => HttpContext.RequestServices.GetService>();
public SignInManager SignInManager => HttpContext.RequestServices.GetService>();
public ApplicationDbContext DbContext => HttpContext.RequestServices.GetService();
}
至于符合内置依赖注入的服务怎么写,请参考官方文档,这里不赘述了。
微软在自家文档里也提到依赖注入的一些高级概念,但就是没实现,这也验证了我前的想法,就是留点空间给下游厂商去发挥,只有大家一起玩才能带动一个生态链么(可惜,相像很美好,这些第三方的组件基本上都是不温不火):
Func
support for lazy initialization 懒加载回调函数跟日志框架一样,也给出一些不错的第三方项目:
本人接触得比较早的是Autofac,下边就简单介绍一下用Autofac默认的IOC容器。还有就是.Net Framework、NetCore 1.0、NetCore 2.0,NetCore 3.0的实现细节都不太一样,这里只举3.0的例子。
AspNetCore 3.X项目里都有一个Program类,这是Web应用的入口,在Main函数里加载Web运行的最基础环境,IOC容器的替换也是在这个地方,也就是将默认的ServiceProvidorFactory换成AutofacServiceProviderFactory:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())//加上这一行
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
});
}
AutoFac增加了一个ConfigureContainer方法,这个方法用于额外的依赖项配置,在这里边注册依赖项用的AutoFac的方式,跟标准的ConfigureServices有所不同,因为本文只讲如何集成Autofac,所以对于具体的使用这里不多讲,想深入了解Autofac,请移步
Registering Components — Autofac 6.0.0 documentation
https://autofaccn.readthedocs.io/en/latest/register/index.html,
下边是ConfigureContainer方法的一个示例:
StartUp.cs
public void ConfigureContainer(ContainerBuilder builder)
{
// Register your own things directly with Autofac here. Don't
// call builder.Populate(), that happens in AutofacServiceProviderFactory
// for you.
//builder.RegisterModule(new MyApplicationModule());
builder.RegisterType().PropertiesAutowired();
builder.RegisterType().PropertiesAutowired();
}
这里边有两行,分别注册一个自定义类Class和一个Controller,并且各自的依赖项都启用了属性注入。这里要注意的是,Controller类型需要在ConfigureServices中申明为Service:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();//调用AddControllersAsServices(),将Controller服务化,这样Autofac才可以访问。
services.AddLogging(loggin=> {
loggin.AddConsole();
loggin.AddLog4Net();
});
}
将默认的依赖注入换Autofac就是这么简单啊。
附:
Class类
public class Class
{
public ILogger Logger { get; set; }
public void SayHello()
{
Logger.LogInformation("Hello World!");
}
}
HomeController类
public class HomeController : Controller
{
ILogger logger;
public Class Test { get; set; }
public HomeController(ILoggerFactory factory)
{
this.logger = factory.CreateLogger("Home");
}
public IActionResult Index()
{
Test.SayHello();
return View();
}
}