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