首页 > 其他分享 >用NetCore + ReactJS 实现一个前后端分离的网站 (2) 用依赖注入实现控制反转

用NetCore + ReactJS 实现一个前后端分离的网站 (2) 用依赖注入实现控制反转

时间:2022-11-21 14:59:24浏览次数:71  
标签:容器 Core 依赖 NovelTogether NetCore 实现 ReactJS cs new

用NetCore + ReactJS 实现一个前后端分离的网站 (2) 用依赖注入实现控制反转

1. 控制反转

刚接触控制反转的时候,颇有些挠头,它怎么就反转了呢。稍微熟悉了之后,才理解了一些。
假设有个方法定义在另一个工程里的某个类中,那么我们本来的做法就是引用这个工程,把那个类new出一个实例来,然后调用它的方法。
我们可以把这个方向(调用者->new->被调用者)称作正向
那么,如果我们申明一个接口,然后用类去实现,并把这两个对象注册到一个容器中,让容器来管理类的实例化。当我们在任何地方需要用到这个接口提供的功能时,直接告诉容器说我要这个接口的具体实现,容器就会提供指定的类的实例,而不用自己new出一个来。
我们可以把这个方向(容器->new->被调用者->调用者)称作反向
总之一句话,不用自己new实例了,而是别人new好了给你,就是控制反转

2. 依赖注入

.NetCore默认支持依赖注入,使用也很简单,代码如下所示。在Program.cs中把服务(接口和类的关系)注册到内置的服务容器IServiceProvider中。

Program.cs
builder.Services.AddScoped<INovelService, NovelService>();

然后在NovelController.cs中通过构造函数注入服务,由容器负责创建和销毁实例。

有三种注入方式:构造函数注入、属性注入、方法注入
有三种生命周期:总是一个实例(Singleton)、一个请求一个实例(Scoped)、每次都是新实例(Transient)

这样NovelController.cs中就不需要再引用命名空间NovelTogether.Core.Service了,只需要引用NovelTogether.Core.IService,说明调用者现在只依赖接口,与实际提供功能的类解耦了。

NovelController.cs
using Microsoft.AspNetCore.Mvc;
using NovelTogether.Core.IService;
using NovelTogether.Core.Model;
//using NovelTogether.Core.Service;

namespace NovelTogether.Core.API.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class NovelController : Controller
    {
        private readonly INovelService _novelService;

        // 通过构造函数注入依赖
        public NovelController(INovelService novelService)
        {
            _novelService = novelService;
        }

        [HttpGet]
        public async Task<List<Novel>> Get()
        {
            // INovelService service = new NovelService();
            // return await service.SelectAsync();

            return await _novelService.SelectAsync();
        }
    }
}

同样的,服务层调用仓储层也可以使用依赖注入。

Program.cs
builder.Services.AddScoped<INovelRepository, NovelRepository>();
NovelService.cs
using NovelTogether.Core.IRepository;
using NovelTogether.Core.IService;
using NovelTogether.Core.Model;
//using NovelTogether.Core.Repository;

namespace NovelTogether.Core.Service
{
    public class NovelService: INovelService
    {
        private readonly INovelRepository _novelRepository;

        public NovelService(INovelRepository novelRepository)
        {
            _novelRepository = novelRepository;
        }

        public async Task<List<Novel>> SelectAsync()
        {
            //INovelRepository da = new NovelRepository();
            //return await da.SelectAsync();

            return await _novelRepository.SelectAsync();
        }
    }
}

到这里,除了API层之外,其余的工程就都只需要引用接口工程,而不需要引用具体的类工程了。引用的工作、或者说依赖关系都转移到了API层,因为需要在这里显式地注册依赖关系。
但这样有个问题,当项目越做越大,接口和类越来越多的时候,在Program.cs中手动添加依赖关系不是一个很优雅的方式,毕竟类已经继承了接口,如果可以识别他们之间关系,然后自动注入容器,不就很方便了吗?
于是,Autofac就登场了。

3. Autofac

Autofac是一个实现控制反转的容器,它的出现比NetCore默认支持的容器要早,功能也更丰富。
我们可以先替换内置的容器,来熟悉一下Autofac的使用。

3.1. 添加nuget包

image

3.2. 更换容器

Program.cs
//builder.Services.AddScoped<INovelRepository, NovelRepository>();
//builder.Services.AddScoped<INovelService, NovelService>();

#region Autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory(containerBuilder =>
{
    containerBuilder.RegisterType<NovelRepository>().As<INovelRepository>().InstancePerLifetimeScope();
    containerBuilder.RegisterType<NovelService>().As<INovelService>().InstancePerLifetimeScope();
}));
#endregion

下面是两种容器的生命周期对比

内置DI Autofac
AddSingleton SingleInstance
AddScoped InstancePerLifetimeScope
AddTransient InstancePerDependency

3.3. 自动注册依赖

从程序执行目录中找到Service.dll和Repository.dll,并把其中所有的类以及依赖的接口注册到容器中。

Program.cs
//builder.Services.AddScoped<INovelRepository, NovelRepository>();
//builder.Services.AddScoped<INovelService, NovelService>();

#region Autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory(containerBuilder =>
{
    //containerBuilder.RegisterType<NovelRepository>().As<INovelRepository>().InstancePerLifetimeScope();
    //containerBuilder.RegisterType<NovelService>().As<INovelService>().InstancePerLifetimeScope();

    foreach (string assemblyPath in Directory.GetFiles(System.AppDomain.CurrentDomain.BaseDirectory, "*.dll", SearchOption.AllDirectories))
    {
        if (new string[] { ".Service.dll", ".Repository.dll" }.Any(x => assemblyPath.EndsWith(x)))
        {
            var assembly = Assembly.LoadFile(assemblyPath);
            containerBuilder.RegisterAssemblyTypes(assembly)
                .AsImplementedInterfaces();
        }
    }
}));
#endregion

这样,再添加新的Service和Repository就不要在Program.cs中手动注册了。
目前,API层添加所有的工程依赖,如下图所示。

image

如果想要更优雅一些,可以删除Repository.dll和Service.dll,通过修改两个工程的Output路径,在编译的时候把dll文件输出到API的目录下面。
image

这个时候,API就只依赖接口以及Common、Model工程了。
image

是不是很漂亮,很符合强迫症患者的要求?
然而,这带来另一个问题,就是平常大家喜欢写完代码直接F5,编译+调试一气呵成。
但是,现在主程序和依赖工程脱节了,F5不会编译那些没添加依赖的工程,导致拿到了旧版本的Repository或者Service,于是还得执行Rebuild All,然后再F5。
不知道大家有没有什么好办法解决这个问题,这里我还是回到上个版本,显式地添加工程依赖项。

4. 结语

这一节介绍了.NetCore内置DI容器和autofac的简单使用,关于实例的生命周期的说明浅尝辄止,并没有展开来说。更加具体的细节问题,当遇到了再去参考大神们细致无比的文章,目前能够跑起来就满足需求了。
下一节要为仓储层添加一个基接口和基类,并实现数据库的接入。

标签:容器,Core,依赖,NovelTogether,NetCore,实现,ReactJS,cs,new
From: https://www.cnblogs.com/wang-yi-yi/p/16903989.html

相关文章

  • antd Cascader实现多选功能
    antd中的组件cascader用于联级选择,但是只支持单选,在项目过程中经常会遇到多选的情况。这边将多选情况进行总结,以及附上实现代码。antd实现的单选功能如下:https://3x.ant.......
  • 生产环境实现Docker部署宝塔面板
    生产环境中,为了避免极小概率的数据丢失,我们将容器内的宝塔文件映射到宿主机的目录中(您之后安装的Nginx、MySQL等服务均会挂载到宿主机目录)。该方法是Docker部署宝塔面......
  • PHP中如何实现进程间通讯
    ​ 进程间通讯机制——IPC(Inter-Process-Communication)。为了使得php5可以使用共享内存和信号量,必须在编译php5程序时激活shmop和sysvsem这两个扩展模块。实现方法:在......
  • Golang中协程调度器底层实现( G、M、P)
    三个必知的核心元素。(G、M、P)G:Goroutine的缩写,一个G代表了对一段需要被执行的Go语言代码的封装M:Machine的缩写,一个M代表了一个内核线程,等同于系统线程P:Processor的缩写......
  • 使用EasyCV Mask2Former轻松实现图像分割
    简介: EasyCV可以轻松预测图像的分割谱以及训练定制化的分割模型。本文主要介绍如何使用EasyCV实现实例分割、全景分割和语义分割,及相关算法思想。作者:贺弘谦言......
  • uniapp使用web-view实现pdf、h5预览
    大家好,我是满心一般​​APP​​​中使用​​pdf​​​预览功能很常见,下面分享下关于​​uniapp​​​中使用​​web-view​​​进行​​pdf​​预览web-view​​​web-view......
  • css实现多余字体用省略号表示
    第一种情况:单行文字超出固定宽度后,用省略号表示<pclass="single">单行文字超出规定宽度后,以省略号形式展示。单行文字超出规定宽度后,以省略号形式展示。</p>.single{......
  • 工程师如何实现西门子PLC S7-1500的远程程序上下载?
    西门子S7-1500系列PLC是系西门子主推的新一代产品。它为企业在自动化控制系统中提供了更高的运行能力,而且简单易用,接上了大量的系统开发时间。同时具备很强的通讯功能和扩展......
  • SpringBoot自定义注解+AOP+redis实现防接口幂等性重复提交,从概念到实战
    一、前言在面试中,经常会有一道经典面试题,那就是:​​怎么防止接口重复提交?​​​小编也是背过的,好几种方式,但是一直没有实战过,做多了管理系统,发现这个事情真的没有过多的重......
  • 使用UDP协议实现简单的分布式日志服务, java和python
    使用UDP协议实现简单的分布式日志服务,java和python这几天系统出现问题,需要查原因.日志分散在各个服务器上,查起来很要命.网上百度了好久,最后发现,各种日志的处理......