兄弟姐妹們,還有三個月就2021年了,給大家提前拜個早年~嘻嘻。
本系列我将梳理ABP VNext中的技術實作,在自我記錄的同時,也幫大家更加深入了解下這個最近非常火的架構。
什麼是ABP VNext這裡就不做介紹了,大家可以去看資料了解下。這裡講解我們實際業務中如何使用該架構。
1.簡介
ABP中依賴注入是基于Microsoft.Extensions.DependencyInjection實作的,在元件開發過程中其實作方式和包括AutoFac在内的第三方類庫大同小異。
在實際的開發使用中,我們并沒有使用第三方類庫,完全基于Microsoft的依賴注入擴充庫進行的元件開發。
2.如何實作依賴注入?
我們先來看下傳統的實作方式:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddTransient<ITestApiService, TestApiService>();
}
可以看到我們需要使用IServiceCollection這個屬性,讓我們來看下IServiceCollection的内部實作:
public class ServiceCollection : IServiceCollection
{
private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
public int Count => _descriptors.Count;
public bool IsReadOnly => false;
public ServiceDescriptor this[int index]
{
get
{
return _descriptors[index];
}
set
{
_descriptors[index] = value;
}
}
// ...
}
可以看到
IServiceCollection
沒有定義其任何成員,而是從
IList<ServiceDescriptor>
派生,本質上是一個集合。
在實際生産過程中,我們肯定不能一個個的手動去注冊,而是希望通過某種約定配置讓其實作自動注冊。
2.如何自定義實作一個依賴注入元件?
首先我們要建立三個接口,分别是L:ITransientDependency、ISingletonDependency、IScopedDependency,分别對應三種生命周期:
public interface ITransientDependency
{
}
public interface ISingletonDependency
{
}
public interface IScopedDependency
{
}
所有需要實作依賴注入的類都需要繼承這其中的某個接口。
然後我們建立一個接口以及實作類:
public interface ITestApiService
{
public string GetResult();
}
public class TestApiService : ITestApiService, ITransientDependency
{
public string GetResult()
{
return "This is test!";
}
}
我們還需要建立一個接口IApplication,用來擷取程式集。(也可以通過其他方式,隻不過這個IApplication後面其他功能會用到)
public interface IApplication
{
}
然後下面是完整實作:
public void DependencyRegister(IServiceCollection services)
{
var assembly = typeof(IApplication).GetTypeInfo().Assembly;
var types = assembly.GetTypes()
.Where(type => type != null
&& type.IsClass
&& !type.IsAbstract
&& !type.IsGenericType).ToArray();
foreach (var type in types)
{
ServiceLifetime? lifeTime = null;
if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
{
lifeTime = ServiceLifetime.Transient;
}
if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
{
lifeTime = ServiceLifetime.Transient;
}
if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
{
lifeTime = ServiceLifetime.Transient;
}
if (!lifeTime.HasValue)
continue;
var serviceTypes = type
.GetCustomAttributes()
.OfType<IExposedServiceTypesProvider>()
.DefaultIfEmpty(new ExposeServicesAttribute
{
IncludeDefaults = true,
IncludeSelf = true
})
.SelectMany(p => p.GetExposedServiceTypes(type))
.ToList();
foreach (var serviceType in serviceTypes)
{
var serviceDescriptor = ServiceDescriptor.Describe(serviceType, type, lifeTime.Value);
services.Add(serviceDescriptor);
}
}
}
public interface IExposedServiceTypesProvider
{
Type[] GetExposedServiceTypes(Type targetType);
}
public class ExposeServicesAttribute : Attribute, IExposedServiceTypesProvider
{
public Type[] ServiceTypes { get; }
public bool? IncludeDefaults { get; set; }
public bool? IncludeSelf { get; set; }
public ExposeServicesAttribute(params Type[] serviceTypes)
{
ServiceTypes = serviceTypes ?? new Type[0];
}
public Type[] GetExposedServiceTypes(Type targetType)
{
var serviceList = ServiceTypes.ToList();
if (IncludeDefaults == true)
{
foreach (var type in GetDefaultServices(targetType))
{
serviceList.AddIfNotContains(type);
}
if (IncludeSelf != false)
{
serviceList.AddIfNotContains(targetType);
}
}
else if (IncludeSelf == true)
{
serviceList.AddIfNotContains(targetType);
}
return serviceList.ToArray();
}
private static List<Type> GetDefaultServices(Type type)
{
var serviceTypes = new List<Type>();
foreach (var interfaceType in type.GetTypeInfo().GetInterfaces())
{
var interfaceName = interfaceType.Name;
if (interfaceName.StartsWith("I"))
{
interfaceName = interfaceName.Right(interfaceName.Length - 1);
}
if (type.Name.EndsWith(interfaceName))
{
serviceTypes.Add(interfaceType);
}
}
return serviceTypes;
}
}
首先我們通過IApplication用反射的方式擷取到程式集中所有需要被檢查的類,然後通過判斷其繼承的接口類型決定需要實作的生命周期。
這裡的預設規則是接口定義層的名稱去掉“I”字元後與實作層類相同,則表明是需要注冊的。
看完是不是突然覺得挺簡單的了!