文章轉載自平娃子(QQ:273206491):http://os.pingwazi.cn/resource/batchinjectservice
一、依賴注入
通過依賴注入,可以實現接口與實現類的松耦合。Asp.Net Core底層設計支持依賴注入。系統中存在的內置服務(Mvc、DbContext等等)的依賴注入和自定義服務的依賴注入。其中內置服務的依賴注入,可以直接調用IServiceCollection的擴展方法(AddMvc()、AddDbContext())。
二、.Net Core底層所實現的依賴注入功能
在使用.Net Core底層所實現的依賴注入功能之前呢,需要先理解依賴注入對象的三種生命周期:
1、Transent(瞬時)生命周期在他們每次請求的時候被創建,這一生命周期適合輕量級和無狀態的服務。並且在一次請求中,如果存在多次獲取這個實例,那這些實例也是不同的。
2、Scoped(范圍)生命周期在每次請求的時候被創建,在一次請求中,如果存在多次獲取這個實例,那么返回的也是同一個實例。
3、Singleton(單例)生命周期在它們第一次被請求的時候使用的時候創建,並且只創建一次,后續都是使用的同一個對象。
本實例使用.Net Core內置的依賴注入功能的步驟如下:
1、使用vs2017或者vs2019創建一個.Net Core WebApi項目
2、創建ISay接口,其中定義一個Say方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public interface ISay
{
string Say();
}
}
3、創建ISay的實現類ChinseSay
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public class ChinseSay : ISay
{
public string Say()
{
Console.WriteLine("我說中國話");
return "我說中國話";
}
}
}
4、在Startup中的ConfigureServices方法中注冊ChineseSay服務。
services.AddTransient<ISay, ChinseSay>();
5、模板控制器中使用依賴注入的對象。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace IocPrictic.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private ISay _ISay;
public ValuesController(ISay isay)
{
this._ISay = isay;
}
// GET api/values
[HttpGet]
public ActionResult<string> Get()
{
string sayResult= this._ISay.Say();
return sayResult;
}
}
}
三、利用反射實現批量的依賴注入
如果需要注入的對象有多個呢?可能是幾十個也可能是幾百個,如果全是認為的進行注入的話,這是一個非常麻煩的事情。因此這里引入了反射來實現批量注入的功能。
實現步驟(思想/算法/...)如下:
1、定義一個需要依賴注入的標記接口(INeedInject),這個接口里面什么也沒有,僅僅標記一個接口需要進行依賴注入。
2、定義三個生命周期接口,這個三個接口里面什么也沒有(ITransentInject、IScopeInject、ISingletonInject),僅僅作為一個類型,在利用反射的時候讓其選擇對應的生命周期注入方式。
3、定義一個不需要依賴注入的標記接口(INoNeedInject),這個接口是在我們需要更換接口實現類的時候,在舊的實現類上實現這個接口,讓反射程序跳過這個實現類,去注入新的類。
4、生命周期接口繼承需要依賴注入的標記接口(INeedInject)。
5、需要依賴注入的接口繼承三種生命周期接口中的其中一種。
6、實現類實現需要依賴注入的接口。
7、利用反射更具標記接口(INeedInject)篩選出那些接口需要進行依賴注入。
8、利用反射找到這個需要進行依賴注入接口的唯一實現類。
9、根據接口定義的注入類型,選擇合適的生命周期類型去實現注入。
10、調用依賴注入的對象
(1)、INeedInject
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public interface INeedInject
{
}
}
(2)、三種生命周期接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public interface ITransentInject:INeedInject
{
}
}
***********************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public interface IScopeInject:INeedInject
{
}
}
***********************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public interface ISingleTonInject:INeedInject
{
}
}
(3)、不需要依賴注入的標記接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public interface INoNeedInject
{
}
}
(4)、定義需要依賴注入的接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public interface ISay:ITransentInject
{
string Say();
}
}
***********************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public interface IEat:ITransentInject
{
string Eat();
}
}
(5)、定義實現類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public class ChinseEat : IEat
{
public string Eat()
{
Console.WriteLine("我吃中國餐");
return "我吃中國餐";
}
}
}
***********************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IocPrictic
{
public class ChinseSay : ISay
{
private IEat _IEat;
public ChinseSay(IEat ieat)
{
this._IEat = ieat;
}
public string Say()
{
string eatResult=_IEat.Eat();
Console.WriteLine("我說中國話");
return $"我說中國話,{eatResult}";
}
}
}
(6)、利用反射實現依賴注入(核心)
在Startup中定義如下方法:
/// <summary>
/// 注冊指定程序集中的服務
/// </summary>
/// <param name="assemblyNames">程序集名的字典</param>
/// <param name="services">IServiceCollection類型的對象</param>
public void BatchInjectService(IDictionary<string,string> assemblyNames,IServiceCollection services)
{
Type iNeedInject = typeof(INeedInject);
Type iTransentInject = typeof(ITransentInject);
Type iScopeInject = typeof(IScopeInject);
Type iSingletonInject = typeof(ISingleTonInject);
Type iNoNeedInject = typeof(INoNeedInject);//當接口切換實現類時,在舊的實現類上實現這個接口就ok
foreach (var assemblyItem in assemblyNames)
{
string assemblyInterName = assemblyItem.Key;
string assemblyObjName = assemblyItem.Key;
Type[] interTypes = Assembly.Load(assemblyInterName).GetTypes().Where(t =>t.IsInterface && iNeedInject.IsAssignableFrom(t) && t!=iNeedInject && t!=iTransentInject && t!=iScopeInject && t!= iSingletonInject).ToArray();
foreach (Type interType in interTypes)
{
Type objType= Assembly.Load(assemblyObjName).GetTypes().Where(t =>t.IsClass && interType.IsAssignableFrom(t) && !iNoNeedInject.IsAssignableFrom(t)).SingleOrDefault();
if (objType == null)
{
throw new Exception($"********************當前接口={interType.Name}沒有找到對應的實現類********************");
}
IList<Type> inJectTypeList = objType.GetInterfaces().Where(i => i == iTransentInject || i == iScopeInject || i == iSingletonInject).ToList();
if (inJectTypeList.Count != 1)
{
throw new Exception($"********************當前接口={interType.Name}沒有找到合適的生命周期類型********************");
}
Type inJectType = inJectTypeList.Single();
string inJectTypeName = inJectType.Name;
switch (inJectTypeName)
{
case "ITransentInject": services.AddTransient(interType, objType); break;
case "IScopeInject": services.AddScoped(interType, objType); break;
case "ISingleTonInject": services.AddSingleton(interType, objType); break;
default: throw new Exception($"********************當前接={interType.Name}沒有指定注入實例的生命周期********************");break;
}
}
}
}
***********************************************
在Startup的ConfigureServices方法中調用批量依賴注入的方法:
//獲取當前程序集名
string currentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
IDictionary<string, string> assemblyNames = new Dictionary<string, string>();
assemblyNames.Add(currentAssemblyName, currentAssemblyName);
//批量注入指定程序集中服務
BatchInjectService(assemblyNames, services);
(7)、在模板控制器中調用調用依賴注入的對象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace IocPrictic.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private ISay _ISay;
public ValuesController(ISay isay)
{
this._ISay = isay;
}
// GET api/values
[HttpGet]
public ActionResult<string> Get()
{
string sayResult= this._ISay.Say();
return sayResult;
}
}
}