.net5 - 创建Web.Api项目(八)IOC依赖注入
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 应用程序

浙公网安备 33010602011771号