在.NET Core 中 依賴注入Dependency-Injection)做爲基礎知識,在.Net Core中無處不在;這麼重要的知識接下來就瞭解和在.Net Core中使用。html
說到依賴注入(Dependency Injection,如下簡稱DI),就必須說IoC(Inverse of Control);這兩個概念很容易搞混。
IoC:主要體現了這樣一種設計思想:經過將一組通用流程的控制從應用轉移到框架之中以實現對流程的複用,同時採用「好萊塢原則」是應用程序以被動的方式實現對流程的定製。git
DI:服務的消費者利用一個獨立的容器(Container)來獲取所需的服務對象,容器自身在提供服務對象的過程當中會自動完成依賴的解析與注入github
核心功能:服務註冊和服務提供web
在.Net Core中主要 Microsoft.Extensions.DependencyInjection 中實現DI相關功能,能夠在你的項目中單獨使用它數據庫
核心分爲兩個組件:IServiceCollection和 IServiceProviderapp
IServiceCollection:負責服務的註冊;主要擴展方法以下: 框架
public static class ServiceCollectionServiceExtensions { public static IServiceCollection AddScoped(this IServiceCollection services, Type serviceType, Type implementationType); public static IServiceCollection AddSingleton(this IServiceCollection services, Type serviceType, Type implementationType); public static IServiceCollection AddTransient(this IServiceCollection services, Type serviceType, Type implementationType); …… }
ServiceCollectionServiceExtensions擴展類中包含了這三個方法的多種重載方法,那麼這三個方法分別什麼含義呢?dom
IServiceProvider:負責服務的獲取;主要方法: async
public interface IServiceProvider { //獲取服務 object GetService(Type serviceType); }
在IServiceCollection中咱們看到了主要到三個方法這些方法的意義是什麼呢?ide
.NET Core DI 爲咱們提供的實例生命周其包括三種:
以上一篇審計日誌源碼做爲示例:
//倉儲接口 public interface IRepository<T> { T Save(T entity); } //審計日誌倉儲實現 public class AuditLogRepository : IRepository<AuditInfo> { AuditLogDBContent _auditLogDB; public AuditLogRepository(AuditLogDBContent auditLogDB) { _auditLogDB = auditLogDB; } public AuditInfo Save(AuditInfo entity) { var retEntity = _auditLogDB.AuditInfos.Add(entity); _auditLogDB.SaveChanges(); return retEntity.Entity; } }
//審計日誌服務接口 public interface IAuditLogService { Task SaveAsync(AuditInfo auditInfo); } //審計日誌服務實現 public class AuditLogService : IAuditLogService { IRepository<AuditInfo> _repository; //依賴注入:IRepository<AuditInfo> public AuditLogService(IRepository<AuditInfo> repository) { _repository = repository; } public async Task SaveAsync(AuditInfo auditInfo) { _repository.Save(auditInfo); } }
public void ConfigureServices(IServiceCollection services) { //審計日誌存儲數據庫 services.AddDbContext<AuditLogDBContent>(options => { string conn = Configuration.GetConnectionString("LogDB"); options.UseSqlite(conn, options => { options.MigrationsAssembly("AuditLogDemo"); }); }); //依賴注入:審計日誌倉儲和審計日誌服務 services.AddScoped<IRepository<AuditInfo>, AuditLogRepository>(); services.AddScoped<IAuditLogService, AuditLogService>(); services.AddControllers(options => { options.Filters.Add(typeof(AuditLogActionFilter)); }); }
能夠看出當前注入服務很少,相對簡單還好,但項目中倉儲和服務數量確定比示例數量多,不可能每一個都像這樣手工注入。
那麼有什麼好的解決辦法呢?接着往下看
public static class DIHelper { /// <summary> /// 注入服務 /// </summary> /// <param name="services"></param> /// <param name="interfaceAssembly"></param> /// <param name="implementAssembly"></param> public static void AddScoped(this IServiceCollection services, Assembly interfaceAssembly, Assembly implementAssembly) { var interfaces = interfaceAssembly.GetTypes().Where(t => t.IsInterface); var implements = implementAssembly.GetTypes(); foreach (var item in interfaces) { var type = implements.FirstOrDefault(x => item.IsAssignableFrom(x)); if (type != null) { services.AddScoped(item, type); } } } /// <summary> /// 注入服務 /// </summary> /// <param name="services"></param> /// <param name="interfaceAssembly"></param> /// <param name="implementAssembly"></param> public static void AddSingleton(this IServiceCollection services, Assembly interfaceAssembly, Assembly implementAssembly) { var interfaces = interfaceAssembly.GetTypes().Where(t => t.IsInterface); var implements = implementAssembly.GetTypes(); foreach (var item in interfaces) { var type = implements.FirstOrDefault(x => item.IsAssignableFrom(x)); if (type != null) { services.AddSingleton(item, type); } } } /// <summary> /// 注入服務 /// </summary> /// <param name="services"></param> /// <param name="interfaceAssembly"></param> /// <param name="implementAssembly"></param> public static void AddTransient(this IServiceCollection services, Assembly interfaceAssembly, Assembly implementAssembly) { var interfaces = interfaceAssembly.GetTypes().Where(t => t.IsInterface); var implements = implementAssembly.GetTypes(); foreach (var item in interfaces) { var type = implements.FirstOrDefault(x => item.IsAssignableFrom(x)); if (type != null) { services.AddTransient(item, type); } } } }
使用方式:
services.AddScoped(Assembly.Load("xxxx.IService"), Assembly.Load("xxxx.Service")); services.AddScoped(Assembly.Load("xxxx.IService"), Assembly.Load("xxxx.Service"));
添加Nuget包:
一、添加Autofac模型實現:
/// <summary> /// 註冊Autofac模塊 /// </summary> public class AutofacModuleRegister : Autofac.Module { /// <summary> /// 重寫Autofac管道Load方法,在這裏註冊注入 /// </summary> protected override void Load(ContainerBuilder builder) { builder.RegisterAssemblyTypes(GetAssemblyByName("AuditLog.EF")) .Where(a => a.Name.EndsWith("Repository")) .AsImplementedInterfaces(); builder.RegisterAssemblyTypes(GetAssemblyByName("AuditLog.Application")) .Where(a => a.Name.EndsWith("Service")) .AsImplementedInterfaces(); //註冊MVC控制器(註冊全部到控制器,控制器注入,就是須要在控制器的構造函數中接收對象) builder.RegisterAssemblyTypes(GetAssemblyByName("AuditLogDemo")) .Where(a => a.Name.EndsWith("Controller")) .AsImplementedInterfaces(); } /// <summary> /// 根據程序集名稱獲取程序集 /// </summary> /// <param name="assemblyName">程序集名稱</param> public static Assembly GetAssemblyByName(string assemblyName) { return Assembly.Load(assemblyName); } }
二、使用Autofac來實現依賴注入
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) //改用Autofac來實現依賴注入 .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
三、調整Statup文件:
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } /// <summary> /// autofac容器 /// </summary> public ILifetimeScope AutofacContainer { get; private set; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //審計日誌存儲 services.AddDbContext<AuditLogDBContent>(options => { string conn = Configuration.GetConnectionString("LogDB"); options.UseSqlite(conn, options => { options.MigrationsAssembly("AuditLogDemo"); }); }); //依賴注入 //Scoped:一個請求建立一個 //services.AddScoped<IRepository<AuditInfo>, AuditLogRepository>(); ////每次建立一個 //services.AddTransient<IAuditLogService, AuditLogService>(); services.AddControllers(options => { options.Filters.Add(typeof(AuditLogActionFilter)); }); } /// <summary> /// 配置容器:在ConfigureServices後執行 /// </summary> /// <param name="builder"></param> public void ConfigureContainer(ContainerBuilder builder) { // 直接用Autofac註冊咱們自定義的 builder.RegisterModule(new AutofacModuleRegister()); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } //autofac 新增 可選 this.AutofacContainer = app.ApplicationServices.GetAutofacRoot(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }
到此Autofac批量實現注入服務完成。
源碼地址:https://github.com/cwsheng/AuditLogDemo
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection
https://www.cnblogs.com/artech/p/di-4-asp-net-core.html