文章轉載自平娃子(QQ:273206491):http://os.pingwazi.cn/resource/batchinjectservice 一、依賴註入 通過依賴註入,可以實現介面與實現類的松耦合。Asp.Net Core底層設計支持依賴註入。系統中存在的內置服務(Mvc、DbContext等 ...
文章轉載自平娃子(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;
}
}
}