欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

DotNetCore依赖注入实现批量注入

程序员文章站 2022-03-20 08:52:09
文章转载自平娃子(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;
        }
    }
}