从壹开始先后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之九 || 依赖注入IoC学习 + AOP界面编程初探

本文3.0版本文章

 
 

 更新

一、若是看不懂本文,或者比较困难,先别着急问问题,我单写了一个关于依赖注入的小Demo,能够下载看看,多思考思考注入的原理:html

https://github.com/anjoy8/BlogArti/tree/master/Blog.Core_IOC%26DI前端

 

二、重要:若是你实现了解耦,也就是 api 层只引用了 IService 和 IRepository 的话,那每次修改 service 层,都须要清理解决方案,从新编译项目,由于这个时候你的api层的dll,仍是以前未修改的代码。vue

 

三、重要+ :请注意,依赖注入的目的不是为了解耦,依赖注入是为了控制反转,通俗来讲,就是不用咱们本身去 new 服务实例了,因此你们不须要必定去解耦(好比下文说到的我没有引用 Service层 和 Repository层),我下一个DDD系列,依赖注入就没有解耦,由于我用的是自带的注入,不是Autofac的反射dll ,我解耦的目的,是为了让你们更好的理解,服务是怎么注入到宿主容器里的。git

 

代码已上传Github+Gitee,文末有地址

  说接上文,上回说到了《八 || API项目总体搭建 6.3 异步泛型+依赖注入初探》,后来的标题中,我把仓储两个字给去掉了,由于好像你们对这个模式颇有不一样的见解,嗯~可能仍是我学艺不精,没有说到其中的好处,如今在学DDD领域驱动设计相关资料,有了好的灵感再给你们分享吧。github

  到目前为止咱们的项目已经有了基本的雏形,后端其实已经能够搭建本身的接口列表了,框架已经有了规模,本来应该说vue了,可是呢,又据说近来Vue-cli已经从2.0升级到了3.0了,还变化挺大,前端大佬们,都不停歇呀。固然我还在学习当中,我也须要了解下有关3.0的特性,但愿给没有接触到,或者刚刚接触到的朋友们,有一些帮助,固然我这个不是随波逐流,只是在众多的博文中,给你们一个入门参考,届时说3.0的时候,仍是会说2.0的相关问题的。sql

  虽然项目总体能够运行了,可是我还有几个小知识点要说下,主要是一、依赖注入和AOP相关知识;二、跨域代理等问题(由于Vue是基于Node开发的,与后台API接口不在同一个地址);三、实体类的DTO相关小问题;四、Redis缓存等;五、部署服务器中的各类坑;虽然都是很小的知识点,我仍是都下给你们说下的,好啦,开始今天的讲解;数据库

零、今天完成的绿色部分

 

 

1、依赖注入的理解和思考

 

更新(19-04-17):若是想好好的理解依赖注入,能够从如下几个方面入手:
一、项目之间引用是如何起做用的,好比为啥 api 层只是引用了 service 层,那为啥也能使用 repository 和 model 等多层的类?编程

二、项目在启动的时候,也就是运行时,是如何动态 获取和访问 每个对象的实例的?也就是 new 的原理后端

三、项目中有 n 个类,对应 m 个实例等,那这些服务,都放在了哪里?确定每个项目都有专属本身的一块。若是项目不启动的话,内存里确定是没有这些服务的。设计模式

四、使用接口(面向抽象)的好处?

五、在项目后期,如何业务须要要所有修改接口的实现类,好比想把 IA = new A();所有  改为 IA = new B();

六、反射的重要性,为何要用到反射 dll ?

 

若是这些每一条本身都能说清楚,那确定就知道依赖注入是干啥的了。

 

说到依赖,我就想到了网上有一个例子,依赖注入和工厂模式中的类似和不一样:

(1)原始社会里,没有社会分工。需要斧子的人(调用者)仅仅能本身去磨一把斧子(被调用者)。相应的情形为:软件程序里的调用者本身建立被调用者。

(2)进入工业社会,工厂出现。斧子再也不由普通人完毕,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。相应软件程序的简单工厂的设计模式。

(3)进入“按需分配”社会,须要斧子的人不须要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就天然出现在他面前。相应Spring的依赖注入

 

 

在上篇文章中,咱们已经了解到了,什么是依赖倒置、控制反转(IOC),什么是依赖注入(DI),网上这个有不少不少的讲解,我这里就不说明了,其实主要是见到这样的,就是存在依赖

public class A : D
{

    public A(B b)
    {
        // do something   
    }
    C c = new C();
}

 

就好比咱们的项目中的BlogController,只要是经过new 实例化的,都是存在依赖

public async Task<List<Advertisement>> Get(int id)
{
    IAdvertisementServices advertisementServices = new AdvertisementServices();
    return await advertisementServices.Query(d => d.Id == id);
}

 

使用依赖注入呢,有如下优势:

  • 传统的代码,每一个对象负责管理与本身须要依赖的对象,致使若是须要切换依赖对象的实现类时,须要修改多处地方。同时,过分耦合也使得对象难以进行单元测试。
  • 依赖注入把对象的创造交给外部去管理,很好的解决了代码紧耦合(tight couple)的问题,是一种让代码实现松耦合(loose couple)的机制。
  • 松耦合让代码更具灵活性,能更好地应对需求变更,以及方便单元测试。

举个栗子,就是关于日志记录的

日志记录:有时须要调试分析,须要记录日志信息,这时能够采用输出到控制台、文件、数据库、远程服务器等;假设最初采用输出到控制台,直接在程序中实例化ILogger logger = new ConsoleLogger(),但有时又须要输出到别的文件中,也许关闭日志输出,就须要更改程序,把ConsoleLogger改为FileLogger或者NoLogger, new FileLogger()或者new SqlLogger() ,此时不断的更改代码,就显得内心很差了,若是采用依赖注入,就显得特别舒畅。

我有一个我的的理解,不知道恰当与否,好比咱们平时食堂吃饭,都是食堂本身炒的菜,就算是配方都同样,控制者(厨师)仍是会有些变化,或者是出错,可是肯德基这种,由总店提供的,店面就失去了控制,就出现了第三方(总店或者工厂等),这就是实现了控制反转,咱们只须要说一句,奥尔良鸡翅,嗯就拿出来一个,并且所有分店的都同样,咱们不用管是否改配方,无论是否依赖哪些调理食材,哈哈。

2、常见的IoC框架有哪些

一、Autofac+原生

我经常使用的仍是原生注入和 Autofac 注入。

Autofac:貌似目前net下用的最多吧
Ninject:目前好像没多少人用了
Unity:也是较为常见

微软 core 自带的 DI

其实.Net Core 有本身的轻量级的IoC框架,

ASP.NET Core自己已经集成了一个轻量级的IOC容器,开发者只须要定义好接口后,在Startup.cs的ConfigureServices方法里使用对应生命周期的绑定方法便可,常见方法以下

services.AddTransient<IApplicationService,ApplicationService>//服务在每次请求时被建立,它最好被用于轻量级无状态服务(如咱们的Repository和ApplicationService服务)

services.AddScoped<IApplicationService,ApplicationService>//服务在每次请求时被建立,生命周期横贯整次请求

services.AddSingleton<IApplicationService,ApplicationService>//Singleton(单例) 服务在第一次请求时被建立(或者当咱们在ConfigureServices中指定建立某一实例并运行方法),其后的每次请求将沿用已建立服务。若是开发者的应用须要单例服务情景,请设计成容许服务容器来对服务生命周期进行操做,而不是手动实现单例设计模式而后由开发者在自定义类中进行操做。

 固然.Net Core自身的容器仍是比较简单,若是想要更多的功能和扩展,仍是须要使用上边上个框架。

 

二、三种注入的生命周期 

权重:

AddSingleton→AddTransient→AddScoped

AddSingleton的生命周期:

项目启动-项目关闭   至关于静态类  只会有一个  

AddScoped的生命周期:

请求开始-请求结束  在此次请求中获取的对象都是同一个 

AddTransient的生命周期:

请求获取-(GC回收-主动释放) 每一次获取的对象都不是同一个

 

这里来个简单的小DEMO:

一、定义四个接口,并分别对其各自接口实现,目的是测试Singleton,Scope,Transient三种,以及最后的 Service 服务:

  public interface ISingTest
    {
        int Age { get; set; }
        string Name { get; set; }
    }

 public class SingTest: ISingTest
 {
     public int Age { get; set; }
     public string Name { get; set; }
 }

//--------------------------

 public interface ISconTest
 {
     int Age { get; set; }
     string Name { get; set; }
 }
  public class SconTest: ISconTest
  {
      public int Age { get; set; }
      public string Name { get; set; }
  }

//--------------------------
 public interface ITranTest
 {
     int Age { get; set; }
     string Name { get; set; }
 }
  public class TranTest: ITranTest
  {
      public int Age { get; set; }
      public string Name { get; set; }
  }

//-----------------------
public interface IAService
{
    void RedisTest();
}

 public class AService : IAService
 {
     private ISingTest sing; ITranTest tran; ISconTest scon;
     public AService(ISingTest sing, ITranTest tran, ISconTest scon)
     {
         this.sing = sing;
         this.tran = tran;
         this.scon = scon;
     }
     public void RedisTest()
     {

     }
 }

 

二、项目注入

 

 

三、控制器调用

  private ISingTest sing; ITranTest tran; ISconTest scon; IAService aService;
  public ValuesController(ISingTest sing, ITranTest tran, ISconTest scon, IAService aService)
  {
      this.sing = sing;
      this.tran = tran;
      this.scon = scon;
      this.aService = aService;
  }

  // GET api/values
  [HttpGet]
  public ActionResult<IEnumerable<string>> SetTest()
  {
      sing.Age = 18;
      sing.Name = "小红";

      tran.Age = 19;
      tran.Name = "小明";

      scon.Age = 20;
      scon.Name = "小蓝";

      aService.RedisTest();


      return new string[] { "value1", "value2" };
  }

  // GET api/values/5
  [HttpGet("{id}")]
  public ActionResult<string> Get(int id)
  {
      return "value";
  }

 

四、开始测试,三种注入方法出现的状况 

请求SetTest // GET api/values

 

AddSingleton的对象没有变

AddScoped的对象没有变化

AddTransient的对象发生变化

------------------------------------------------------------

请求 // GET api/values/5

 

AddSingleton的对象没有变

AddScoped的对象发生变化

AddTransient的对象发生变化

 

注意:

因为AddScoped对象是在请求的时候建立的

因此不能在AddSingleton对象中使用

甚至也不能在AddTransient对象中使用

 

因此权重为

AddSingleton→AddTransient→AddScoped

 

否则则会抛以下异常

 

3、较好用的IoC框架使用——Autofac

首先呢,咱们要明白,咱们注入是要注入到哪里——Controller API层。而后呢,咱们看到了在接口调用的时候,若是须要其中的方法,须要using两个命名空间

     [HttpGet("{id}", Name = "Get")]
        public async Task<List<Advertisement>> Get(int id)
        {
            IAdvertisementServices advertisementServices = new AdvertisementServices();//须要引用两个命名空间Blog.Core.IServices;Blog.Core.Services;

            return await advertisementServices.Query(d => d.Id == id);
        }

 

接下来咱们就须要作处理:

 

一、引入nuget包

在Nuget中引入两个:Autofac.Extras.DynamicProxy(Autofac的动态代理,它依赖Autofac,因此能够不用单独引入Autofac)、Autofac.Extensions.DependencyInjection(Autofac的扩展)

 

 

二、接管ConfigureServices

让Autofac接管Starup中的ConfigureServices方法,记得修改返回类型IServiceProvider

 

 

     public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            #region 配置信息
            //Blog.Core.Repository.BaseDBConfig.ConnectionString = Configuration.GetSection("AppSettings:SqlServerConnection").Value;
            #endregion

            #region Swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info
                {
                    Version = "v0.1.0",
                    Title = "Blog.Core API",
                    Description = "框架说明文档",
                    TermsOfService = "None",
                    Contact = new Swashbuckle.AspNetCore.Swagger.Contact { Name = "Blog.Core", Email = "Blog.Core@xxx.com", Url = "https://www.jianshu.com/u/94102b59cc2a" }
                });

                //就是这里

                #region 读取xml信息
                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "Blog.Core.xml");//这个就是刚刚配置的xml文件名
                var xmlModelPath = Path.Combine(basePath, "Blog.Core.Model.xml");//这个就是Model层的xml文件名
                c.IncludeXmlComments(xmlPath, true);//默认的第二个参数是false,这个是controller的注释,记得修改
                c.IncludeXmlComments(xmlModelPath);
                #endregion

                #region Token绑定到ConfigureServices
                //添加header验证信息
                //c.OperationFilter<SwaggerHeader>();
                var security = new Dictionary<string, IEnumerable<string>> { { "Blog.Core", new string[] { } }, };
                c.AddSecurityRequirement(security);
                //方案名称“Blog.Core”可自定义,上下一致便可
                c.AddSecurityDefinition("Blog.Core", new ApiKeyScheme
                {
                    Description = "JWT受权(数据将在请求头中进行传输) 直接在下框中输入{token}\"",
                    Name = "Authorization",//jwt默认的参数名称
                    In = "header",//jwt默认存放Authorization信息的位置(请求头中)
                    Type = "apiKey"
                }); 
                #endregion


            });
            #endregion

            #region Token服务注册
            services.AddSingleton<IMemoryCache>(factory =>
             {
                 var cache = new MemoryCache(new MemoryCacheOptions());
                 return cache;
             });
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Admin", policy => policy.RequireClaim("AdminType").Build());//注册权限管理,能够自定义多个
            });
            #endregion

            #region AutoFac
            
            //实例化 AutoFac  容器   
            var builder = new ContainerBuilder();

            //注册要经过反射建立的组件
            builder.RegisterType<AdvertisementServices>().As<IAdvertisementServices>();

            //将services填充到Autofac容器生成器中
            builder.Populate(services);

            //使用已进行的组件登记建立新容器
            var ApplicationContainer = builder.Build();

            #endregion

            return new AutofacServiceProvider(ApplicationContainer);//第三方IOC接管 core内置DI容器
        }

 

这个时候咱们就把AdvertisementServices的new 实例化过程注入到了Autofac容器中,

这个时候要看明白,前边的是实现类,后边的是接口,顺序不要搞混了。

 

三、构造函数方式来注入

依赖注入有三种方式(构造方法注入、setter方法注入和接口方式注入),咱们平时基本都是使用其中的构造函数方式实现注入,

在BlogController中,添加构造函数,并在方法中,去掉实例化过程;

     readonly IAdvertisementServices _advertisementServices;
/// <summary> /// 构造函数 /// </summary> /// <param name="advertisementServices"></param> public BlogController(IAdvertisementServices advertisementServices) { _advertisementServices = advertisementServices; }
    [HttpGet(
"{id}", Name = "Get")] public async Task<List<Advertisement>> Get(int id) { //IAdvertisementServices advertisementServices = new AdvertisementServices();//须要引用两个命名空间Blog.Core.IServices;Blog.Core.Services; return await _advertisementServices.Query(d => d.Id == id); }

 

 

四、效果调试,已经成功

而后运行调试,发如今断点刚进入的时候,接口已经被实例化了,达到了注入的目的。

 

注意:这里常常会遇到一个错误:None of the constructors found with ........,

查看你的service服务,是否是用了其余的仓储repository,可是又缺乏了构造函数。

 

 

若是没有问题,你们就须要想一想,除了 Autofac 还有没有其余的不用第三方框架的注入方法呢?聪明如你,netcore 还真自带了注入扩展。

 

五、NetCore 自带的注入实现效果

固然,咱们用 Asp.net core 自带的注入方式也是能够的,也挺简单的,这里先说下使用方法:

 // 注入 service
 services.AddScoped<IAdvertisementServices, AdvertisementServices>();

 // 注入 repository
 services.AddScoped<IAdvertisementRepository, AdvertisementRepository>();

 

这个时候,咱们发现已经成功的注入了,并且特别简单,那为啥还要使用 Autofac 这种第三方扩展呢,咱们想想,上边咱们仅仅是注入了一个 Service ,可是项目中有那么多的类,都要一个个手动添加么,多累啊,答案固然不是滴~

 

 

4、整个 dll 程序集的注入

一、服务程序集注入方式 —— 未解耦

经过反射将 Blog.Core.Services 和 Blog.Core.Repository 两个程序集的所有方法注入

修改以下代码,注意这个时候须要在项目依赖中,右键,添加引用 Blog.Core.Services 层和 Repository 层 到项目中,以下图,这个时候咱们的程序依赖了具体的服务

 

核心代码以下,注意这里是 Load 模式(程序集名):

       var builder = new ContainerBuilder();

            //注册要经过反射建立的组件
            //builder.RegisterType<AdvertisementServices>().As<IAdvertisementServices>();

            var assemblysServices = Assembly.Load("Blog.Core.Services");//要记得!!!这个注入的是实现类层,不是接口层!不是 IServices
            builder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces();//指定已扫描程序集中的类型注册为提供全部其实现的接口。
            var assemblysRepository = Assembly.Load("Blog.Core.Repository");//模式是 Load(解决方案名)
            builder.RegisterAssemblyTypes(assemblysRepository).AsImplementedInterfaces();

            //将services填充到Autofac容器生成器中
            builder.Populate(services);

            //使用已进行的组件登记建立新容器
            var ApplicationContainer = builder.Build();

 

其余不变,运行项目,一切正常,换其余接口也能够

 

 

到这里,Autofac依赖注入已经完成,基本的操做就是这样,不过可能你尚未真正体会到注入的好处,挑战下吧,看看下边的内容,将层级进行解耦试试!

 

 


 

二、程序集注入 —— 实现层级解耦

 

这是一个学习的思路,你们要多想一想,可能会感受无聊或者没用,可是对理解项目启动和加载,仍是颇有必要的。

一、项目最终只依赖抽象

最终的效果是这样的:工程只依赖抽象,把两个实现层删掉,引用这两个接口层。

 

 

二、配置仓储和服务层的程序集输出

将 Blog.Repository 层和 Service 层项目生成地址改为相对路径,这样你们就不用手动拷贝这两个 dll 了,F6编译的时候就直接生成到了 api 层 bin 下了:


“...\Blog.Core\bin\Debug\”

 

 

三、使用 LoadFile 加载服务层的程序集

 var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;//获取项目路径
 var servicesDllFile = Path.Combine(basePath, "Blog.Core.Services.dll");//获取注入项目绝对路径
 var assemblysServices = Assembly.LoadFile(servicesDllFile);//直接采用加载文件的方法

 

这个时候,可能咱们编译成功后,页面能正常启动,证实咱们已经把 Service 和 Repository 两个服务层的全部服务给注册上了,可是访问某一个接口,仍是会出现错误:

 

 

这个错误表示,咱们的 SqlSugar 服务,没有被注册成功,那确定就是咱们的 Sqlsugar 程序集没有正常的引用,怎么办呢,这里有两种方法,请往下看。

 

四、Sqlsugar 加载失败,方式一:api层引用 sugar 

你能够直接在 api 层,添加对 sqlsugar 的使用,也能够直接不用修改,好比个人项目,多级之间存在级联的关系,api >> IService >> Model >> sqlSugar :

 

缘由:在 Api层,引入 sqlSugarCore nuget 包,由于存在层级是存在依赖的

注意!这个地方不少人不理解为啥:

解耦,并非啥直接不用了!
解耦仅仅是去掉引用耦合,目的是不用在修改了service.dll 层的某一个方法的时候,而停到api.dll这个整个服务,
当项目启动的时候,仍是须要将全部的服务都注册到主机里,
autofac依赖注入,仅仅是用反射的方法,将service.dll 和 repository.dll 项目服务进行注册,这个过程和引用是同样的,由于若是你引用,当项目编译或启动的时候,也是把层服务所有注入到主机的过程,
因此sqlsugar仍是须要引用,由于须要这个组件的服务。

 

五、方式二:不引用Sugar包!可是须要拷贝 .dll 文件 

若是你就想要 api 层干净,就是不想引用 sqlsugar 层的话,那就除非是把 sugar下的全部dll文件都拷贝进去,其实这样也是能够的,只要把第三方的nuget包生成的dll文件所有拷贝就行,你能够看下,sqlsugar依赖了不少dll

 

可是这个时候咱们须要使用 LoadFrom 模式,由于咱们上边使用的是 LoadFile 做为反射加载,这样的话,有一个问题,就是Repository层引用的 sqlsugar 会提示找不到,那咱们就换一个反射加载方式 —— LoadFrom:

//两者的区别

Assembly.LoadFile只载入相应的dll文件,好比Assembly.LoadFile("a.dll"),则载入a.dll,假如a.dll中引用了b.dll的话,b.dll并不会被载入。 Assembly.LoadFrom则不同,它会载入dll文件及其引用的其余dll,好比上面的例子,b.dll也会被载入。

 

哦!是否是瞬间想到了什么,没错就是 Sqlsugar 加载问题,咱们换成 LoadFrom ,而且删除引用 sqlsugar 来看看,可是!必定要把 sqlsugar.dll 和 它依赖的 MySql.Data.dll 给拷贝进来api层;

因此整体来讲有三个状况:

一、loadfile 模式 + 引用 SqlSugar
二、loadfile 模式+ 引用 
三、loadfrom 模式+ 拷贝 .dll 文件

 

 

 

 

六、链接字符串问题

注意:若是采用上边的方法,把 service 和 Repository 层虽然解耦了,可是必须采用 LoadFile( dll 文件) 的形式,这样就致使了,在 startup.cs 启动中,没法给其余类库中的静态属性赋值的能力,好比:

            BaseDBConfig.ConnectionString = "数据库链接字符串";

这个在 startup.cs 的ConfigureServices 方法中,是没法生效的。解决办法:

一、不解耦,仍是采用普通办法,引用两个层,用 Assembly.Load("Blog.Core.Services") 方式;

二、按照上边解耦,可是数据库链接字符串配置,须要在 Repostory 层

// public static string ConnectionString { get; set; }

public static string ConnectionString = Appsettings.app(new string[] { "AppSettings", "RedisCaching", "ConnectionString" });//获取链接字符串

 

七、解除Service层和Repository层之间的耦合

 还记得Blog.Core.Services中的BaseServices.cs么,它仍是经过new 实例化的方式在建立,仿照contrller,修改BaseServices并在所有子类的构造函数中注入:

   public class BaseServices<TEntity> : IBaseServices<TEntity> where TEntity : class, new()
    {
        //public IBaseRepository<TEntity> baseDal = new BaseRepository<TEntity>();
        public IBaseRepository<TEntity> baseDal;//经过在子类的构造函数中注入,这里是基类,不用构造函数
      //...  
   }


    public class AdvertisementServices : BaseServices<Advertisement>, IAdvertisementServices
    {
        IAdvertisementRepository dal;
        public AdvertisementServices(IAdvertisementRepository dal)
        {
            this.dal = dal;
            base.baseDal = dal;
        }       
    }

 

 

好啦,如今整个项目已经完成了相互直接解耦的功能,之后就算是Repository和Service如何变化,接口层都不用修改,由于已经完成了注入,第三方Autofac会作实例化的过程。

 

八、容器内查看注入的服务数据

若是你想看看是否注入到容器里成功了,能够直接看看容器 ApplicationContainer 的内容:

 

 

5、 无接口项目注入

一、接口形式的注入

上边咱们讨论了不少,可是都是接口框架的,

好比:Service.dll 和与之对应的 IService.dll,Repository.dll和与之对应的 IRepository.dll,

这样,咱们在多层之间使用服务的话,直接将咱们须要使用的 new 对象,注入到容器里,而后咱们就可使用相应的接口了,

好比:咱们想在 controller 里使用AdvertisementServices 类,那咱们就能够直接使用它的接口 IAdvertisementServices,这样就很好的达到了解耦的目的,这样咱们就能够在API层,就轻松的把 Service.dll 给解耦了;

若是咱们须要在 Service类里,使用 AdvertisementRepository ,咱们就直接使用对应的接口 IAdvertisementRepository,这样,咱们就从 Service 层中,把仓储层给解耦了。

 

 

二、若是没有接口

案例是这样的:

 若是咱们的项目是这样的,没有接口,会怎么办:

    // 服务层类 
   public class StudentService
    {
        StudentRepository _studentRepository;
        public StudentService(StudentRepository studentRepository)
        {
            _studentRepository = studentRepository;
        }


        public string Hello()
        {
            return _studentRepository.Hello();
        }

    }


    // 仓储层类
     public class StudentRepository
    {
        public StudentRepository()
        {

        }

        public string Hello()
        {
            return "hello world!!!";
        }

    }


    // controller 接口调用
 StudentService _studentService;

    public ValuesController(StudentService studentService)
    {
        _studentService = studentService;
    }

 

这样的话,咱们就不能使用上边的接口注入模式了,由于咱们上边是把注入的服务,对应注册给了接口了 .AsImplementedInterfaces() ,咱们就没法实现解耦了,由于根本没有了接口层,因此咱们只能引用实现类层,这样注入:

 

 

经过 builder.RegisterAssemblyTypes(assemblysRepository); 方法直接注入服务,没有其余的东西。

 若是你须要这个小demo,能够从个人Github里下载:https://github.com/anjoy8/Blog.Core/blob/master/Blog.Core/wwwroot/NoInterAutofacIOC.rar

 

三、若是是没有接口的单独实体类

 

    public class Love
    {
        // 必定要是虚方法
        public virtual string SayLoveU()
        {
            return "I ♥ U";
        }

    }

//---------------------------

////只能注入该类中的虚方法
builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love)))
    .EnableClassInterceptors()
    .InterceptedBy(typeof(BlogLogAOP));

 

6、同一接口多实现类注入

这里暂时没有实例代码,若是你正好须要,能够看看这个博友的栗子:https://www.cnblogs.com/fuyujian/p/4115474.html

我会在以后的时间写个栗子放到这里。 

 

一种:动态方式

// 定义一个接口,两个实现类
 public interface IDemoService
 {
     string Get();
 }

 public class DemoServiceA : IDemoService
 {
     public string Get()
     {
         return "Service A";
     }
 }

 public class DemoServiceB : IDemoService
 {
     public string Get()
     {
         return "Service B";
     }
 }


// 依赖注入
services.AddSingleton(factory =>
{
    Func<string, IDemoService> accesor = key =>
    {
        if (key.Equals("ServiceA"))
        {
            return factory.GetService<DemoServiceA>();
        }
        else if (key.Equals("ServiceB"))
        {
            return factory.GetService<DemoServiceB>();
        }
        else
        {
            throw new ArgumentException($"Not Support key : {key}");
        }
    };
    return accesor;
});


// 使用
private IDemoService _serviceA;
private IDemoService _serviceB;
private readonly Func<string, IDemoService> _serviceAccessor;

public ValuesController(Func<string, IDemoService> serviceAccessor)
{
    this._serviceAccessor = serviceAccessor;
    _serviceA = _serviceAccessor("ServiceA");
    _serviceB = _serviceAccessor("ServiceB");

}

 

二种,工厂模式

    // 设计工厂
    public class SingletonFactory
    {
 
        Dictionary<Type, Dictionary<string, object>> serviceDict;
        public SingletonFactory()
        {
            serviceDict = new Dictionary<Type, Dictionary<string, object>>();
        }
 
        public TService GetService<TService>(string id) where TService : class
        {
            var serviceType = typeof(TService);
            return GetService<TService>(serviceType, id);
        }
 
        public TService GetService<TService>(Type serviceType, string id) where TService : class
        {
            if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict))
            {
                if (implDict.TryGetValue(id, out object service))
                {
                    return service as TService;
                }
            }
            return null;
        }
 
        public void AddService<TService>(TService service, string id) where TService : class
        {
            AddService(typeof(TService), service, id);
        }
 
        public void AddService(Type serviceType, object service, string id)
        {
            if (service != null)
            {
                if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict))
                {
                    implDict[id] = service;
                }
                else
                {
                    implDict = new Dictionary<string, object>();
                    implDict[id] = service;
                    serviceDict[serviceType] = implDict;
                }
            }
        }
    }


            // 注入工厂

            SingletonFactory singletonFactory = new SingletonFactory();
            singletonFactory.AddService<IServiceA>(new ImplA1(), "impla1");
            singletonFactory.AddService<IServiceA>(new ImplA2(), "impla2");
 
            services.AddSingleton(singletonFactory);



        // 使用
        private readonly IServiceA serviceA;
        public HomeController(SingletonFactory singletonFactory)
        {
            this.serviceA = singletonFactory.GetService<IServiceA>("impla2"); //使用标识从SingletonFactory获取本身想要的服务实现
        }

 

 

 

 

7、简单了解经过AOP切面实现日志记录

什么是AOP?引用百度百科:AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。实现AOP主要由两种方式,

一种是编译时静态植入,优势是效率高,缺点是缺少灵活性,.net下postsharp为表明者(好像是付费了。。)。

另外一种方式是动态代理,优势是灵活性强,可是会影响部分效率,动态为目标类型建立代理,经过代理调用实现拦截。

AOP能作什么,常见的用例是事务处理、日志记录等等。

常见的AOP都是配合在Ioc的基础上进行操做,上边我们讲了Autofac这个简单强大的Ioc框架,下面就讲讲Autofac怎么实现AOP。Autofac的AOP是经过Castle(也是一个容器)项目的核心部分实现的,名为Autofac.Extras.DynamicProxy,顾名思义,其实现方式为动态代理。固然AOP并不必定要和依赖注入在一块儿使用,自身也能够单独使用。

网上有一个博友的图片,大概讲了AOP切面编程

 

 

8、结语

  今天的文章呢,主要说了依赖注入IoC在项目中的使用,从上边的说明中,能够看到,最大的一个优势就是实现解耦的做用,最明显的就是,在Controller中,不用在实例服务层或者业务逻辑层了,不过仍是有些缺点的,缺点之一就是会占用必定的时间资源,效率稍稍受影响,不过和总体框架相比,这个影响应该也是值得的。

  明天,咱们继续将面向切面编程AOP中的,日志记录和面向AOP的缓存使用。

 

好文参考:http://www.javashuo.com/article/p-ohmxlmzc-dq.html

 

 

9、CODE

 https://github.com/anjoy8/Blog.Core

https://gitee.com/laozhangIsPhi/Blog.Core