NuGet包:
Microsoft.Extensions.DependencyModel Microsoft.Extensions.Options
XXX.Common項目下新建文件夾【DependencyInjection】
新建類:RuntimeHelper、ServiceExtension
using Microsoft.Extensions.DependencyModel; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Loader; namespace NetFive.Common.DependencyInjection { public class RuntimeHelper { /// <summary> /// 獲取項目程序集,排除所有的系統程序集(Microsoft.***、System.***等)、Nuget下載包 /// </summary> /// <returns></returns> public static IList<Assembly> GetAllAssemblies() { var list = new List<Assembly>(); var deps = DependencyContext.Default; var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系統程序集、Nuget下載包 foreach (var lib in libs) { try { var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); list.Add(assembly); } catch (Exception) { } } return list; } public static Assembly GetAssembly(string assemblyName) { return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName)); } public static IList<Type> GetAllTypes() { var list = new List<Type>(); foreach (var assembly in GetAllAssemblies()) { var typeInfos = assembly.DefinedTypes; foreach (var typeInfo in typeInfos) { list.Add(typeInfo.AsType()); } } return list; } public static IList<Type> GetTypesByAssembly(string assemblyName) { var list = new List<Type>(); var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName)); var typeInfos = assembly.DefinedTypes; foreach (var typeInfo in typeInfos) { list.Add(typeInfo.AsType()); } return list; } public static Type GetImplementType(string typeName, Type baseInterfaceType) { return GetAllTypes().FirstOrDefault(t => { if (t.Name == typeName && t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name)) { var typeInfo = t.GetTypeInfo(); return typeInfo.IsClass && !typeInfo.IsAbstract; } return false; }); } } }
using Microsoft.Extensions.DependencyInjection; using System; using System.Linq; using System.Reflection; namespace NetFive.Common.DependencyInjection { /// <summary> /// IServiceCollection擴展 /// </summary> public static class ServiceExtension { /// <summary> /// 注冊服務 /// </summary> /// <param name="services"></param> /// <param name="interfaceAssemblyName">定義業務接口的程序集名稱</param> /// <param name="implementAssemblyName">實現業務接口的程序集名稱(默認 interfaceAssemblyName)</param> public static void RegisterService(this IServiceCollection service, string interfaceAssemblyName, string implementAssemblyName) { if (string.IsNullOrEmpty(implementAssemblyName)) { RegisterAssembly(service, interfaceAssemblyName); } else { RegisterAssembly(service, interfaceAssemblyName, implementAssemblyName); } } /// <summary> /// 批量注入接口程序集中對應的實現類。 /// <para> /// 需要注意的是,這里有如下約定: /// IUserService --> UserService, IUserRepository --> UserRepository. /// </para> /// </summary> /// <param name="service"></param> /// <param name="interfaceAssemblyName">接口程序集的名稱(不包含文件擴展名)</param> /// <returns></returns> internal static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName) { if (service == null) { throw new ArgumentNullException(nameof(service)); } if (string.IsNullOrEmpty(interfaceAssemblyName)) { throw new ArgumentNullException(nameof(interfaceAssemblyName)); } var assembly = RuntimeHelper.GetAssembly(interfaceAssemblyName); if (assembly == null) { throw new DllNotFoundException($"the dll \"{interfaceAssemblyName}\" not be found"); } //過濾掉非接口及泛型接口 var types = assembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface); foreach (var type in types) { var implementTypeName = type.Name.Substring(1); var implementType = RuntimeHelper.GetImplementType(implementTypeName, type); if (implementType != null) service.AddScoped(type, implementType); } return service; } /// <summary> /// 用DI批量注入接口程序集中對應的實現類。 /// </summary> /// <param name="service"></param> /// <param name="interfaceAssemblyName">接口程序集的名稱(不包含文件擴展名)</param> /// <param name="implementAssemblyName">實現程序集的名稱(不包含文件擴展名)</param> /// <returns></returns> internal static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName, string implementAssemblyName) { if (service == null) throw new ArgumentNullException(nameof(service)); if (string.IsNullOrEmpty(interfaceAssemblyName)) throw new ArgumentNullException(nameof(interfaceAssemblyName)); if (string.IsNullOrEmpty(implementAssemblyName)) throw new ArgumentNullException(nameof(implementAssemblyName)); var interfaceAssembly = RuntimeHelper.GetAssembly(interfaceAssemblyName); if (interfaceAssembly == null) { throw new DllNotFoundException($"the dll \"{interfaceAssemblyName}\" not be found"); } var implementAssembly = RuntimeHelper.GetAssembly(implementAssemblyName); if (implementAssembly == null) { throw new DllNotFoundException($"the dll \"{implementAssemblyName}\" not be found"); } //過濾掉非接口及泛型接口 var types = interfaceAssembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface); foreach (var type in types) { //過濾掉抽象類、泛型類以及非class var implementType = implementAssembly.DefinedTypes .FirstOrDefault(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any(b => b.Name == type.Name)); if (implementType != null) { service.AddScoped(type, implementType.AsType()); } } return service; } } }
Startup注冊:
#region IOC依賴注入自定義 services.RegisterService("NetFive.Repository", string.Empty); services.RegisterService("NetFive.Command", string.Empty); services.RegisterService("NetFive.Query", string.Empty); services.RegisterService("NetFive.Service", string.Empty); #endregion
NuGet包:
Autofac Autofac.Extensions.DependencyInjection
Program.cs
.UseServiceProviderFactory(new AutofacServiceProviderFactory())//添加Autofac服務
Startup類添加 ConfigureContainer() 方法,必須添加在Startup類里面沒有細研究其他方法
/// <summary> /// 這段代碼必須放在Startup類里面 /// </summary> /// <param name="builder"></param> public void ConfigureContainer(ContainerBuilder builder) { }
構造函數注入:
//構造函數注入 builder.RegisterType<EmployeeService>().As<IEmployeeService>();
構造函數使用:
屬性注入:
builder.RegisterType<EmployeeService>().As<IEmployeeService>().PropertiesAutowired();//只能在當前的EmployeeService類,使用屬性注入
屬性注入使用:
方法注入:
//方法注入 builder.RegisterType<EmployeeQry>().As<IEmployeeQry>(); builder.RegisterType<EmployeeService>().OnActivated(e => e.Instance.Qry(e.Context.Resolve<IEmployeeQry>())).As<IEmployeeService>();
方法注入使用:
需要 using 命名空間 System.Reflection
修改 Straup.cs 文件中的 ConfigureContainer() 方法
約定接口(Interface)和實現(class)都是以 Service 【或者其他】結尾的。
/// <summary> /// 這段代碼必須放在Startup類里面 /// </summary> /// <param name="builder"></param> public void ConfigureContainer(ContainerBuilder builder) { //批量注入//加載程序集 //var urpIService = Assembly.Load("NetFive.Service"); //接口層 //var urpService = Assembly.Load("NetFive.Service"); //實現層 //根據名稱約定(服務層的接口和實現均以Service結尾),實現服務接口和服務實現的依賴 //builder.RegisterAssemblyTypes(urpIService, urpService).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().PropertiesAutowired(); //泛型注冊 builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); //批量注入 var repository = Assembly.Load("NetFive.Repository"); builder.RegisterAssemblyTypes(repository).Where(t => t.Name.EndsWith("UnitOfWorks") || t.Name.EndsWith("DbContext")).AsImplementedInterfaces(); var qry = Assembly.Load("NetFive.Query"); builder.RegisterAssemblyTypes(qry).Where(t => t.Name.EndsWith("Qry")).AsImplementedInterfaces(); var cmd = Assembly.Load("NetFive.Command"); builder.RegisterAssemblyTypes(cmd).Where(t => t.Name.EndsWith("Cmd")).AsImplementedInterfaces(); //批量注入//加載程序集 var urpIService = Assembly.Load("NetFive.Service"); //因為接口層和實現層都在一起,所以只用寫一個 builder.RegisterAssemblyTypes(urpIService).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().PropertiesAutowired(); }
使用:
#region 指定控制器的實例由容器來創建 services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); #endregion
#region 注冊所有控制器的關系及控制器實例化所需要的組件 var controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes() .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray(); builder.RegisterTypes(controllersTypesInAssembly) .PropertiesAutowired(); #endregion
使用:
擴展:自己控制哪些屬性需要做依賴注入(默認是讓控制器中的屬性都依賴注入)
using System; namespace NetFive.WebApi.Custom.Autofacs { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class AutowaredAttribute : Attribute { } }
using Autofac.Core; using System.Linq; using System.Reflection; namespace NetFive.WebApi.Custom.Autofacs { public class PropertySelector : IPropertySelector { public bool InjectProperty(PropertyInfo propertyInfo, object instance) { return propertyInfo.CustomAttributes.Any(ca => ca.AttributeType == typeof(AutowaredAttribute)); } } }
添加特性才能使用:
//方式1.以泛型方式注冊 builder.RegisterGeneric(typeof(UnitOfWork<>)).As(typeof(IUnitOfWork<>)).SingleInstance(); builder.RegisterGeneric(typeof(DatabaseFactory<>)).As(typeof(IDatabaseFactory<>)).SingleInstance(); //方式2.以普通的方式注冊 //builder.RegisterType<UnitOfWork<AutofacDBEntities>>().As<IUnitOfWork<AutofacDBEntities>>().SingleInstance(); //builder.RegisterType<DatabaseFactory<AutofacDBEntities>>().As<IDatabaseFactory<AutofacDBEntities>>().SingleInstance();
1、InstancePerDependency :默認模式,每次調用,都會重新實例化對象;每次請求都創建一個新的對象;
builder.RegisterType<TestServiceA>().As<ITestServiceA>().InstancePerDependency();
2、SingleInstance :單例模式,每次調用,都會使用同一個實例化的對象;每次都用同一個對象;
builder.RegisterType<TestServiceA>().As<ITestServiceA>().SingleInstance();
3、InstancePerLifetimeScope : 同一個生命周期域中,每次調用,都會使用同一個實例化的對象;每次都用同一個對象;且每個不同的生命周期域中的實例是唯一的,不共享的。
builder.RegisterType<TestServiceA>().As<ITestServiceA>().InstancePerLifetimeScope();
4、InstancePerMatchingLifetimeScope : 同一個匹配的生命周期域中,每次調用,都會使用同一個實例化的對象;每次都用同一個對象;且每個不匹配的生命周期域中的實例是唯一的,不共享的。
builder.RegisterType<TestServiceA>().As<ITestServiceA>().InstancePerMatchingLifetimeScope();
5、InstancePerOwned : 在一個所擁有的實例創建的生命周期中,每次調用,都會使用同一個實例化的對象;每次都用同一個對象;(較少使用)
6、InstancePerHttpRequest : 同一次Http請求上下文中,每次調用,都會使用同一個實例化的對象;每次都用同一個對象;僅適用於 ASP.NET (CORE) MVC 或 WebForm 應用程序