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 应用程序