美文网首页
DotNetCore依赖注入实现批量注入

DotNetCore依赖注入实现批量注入

作者: pingwazi | 来源:发表于2019-04-09 15:13 被阅读0次

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

    相关文章

      网友评论

          本文标题:DotNetCore依赖注入实现批量注入

          本文链接:https://www.haomeiwen.com/subject/ruobiqtx.html