DotNetCore依賴注入實現批量注入


文章轉載自平娃子(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;
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM