【To .NET】.NET Core Web API开发流程知识点整理[基础]

大家好!我是未来村村长,就是那个“请你跟我这样做,我就跟你这样做!”的村长👨‍🌾!

👩‍🌾“人生苦短,你用Python”,“Java内卷,我用C#”。

一、Web API项目结构

1、框架说明

​ Web API项目基于两个框架:Microsoft.AspNetCore.App和Microsoft.NETCore.App

(1)项目入口:Program.cs

​ 在Program.cs中,包含两个方法CreateHostBuilder和Main。在CreateHostBuilder主要完成两件事,一是加载Startup配置类,二是返回HostBuilder对象。在Main方法中,通过调用CreateHostBuilder方法得到的HostBuilder来Build一个Host对象作为Web API运行的容器,通过Run来启动。

namespace EFLearn.API
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}
(2)项目启动配置:Startup

​ Startup也有两个关键方法:ConfigureServices和Configure,ConfigureServices用于向容器添加服务,Configure用于配置HTTP请求管道,例如我们使用swagger时,需要通过services.AddSwaggerGen来进行服务的配置, 通过app.UseSwagger()进行服务的使用。

namespace EFLearn.API
{
    public class Startup
    {
        //构造器
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }

		//此方法由运行时调用。使用此方法向容器添加服务。
        public void ConfigureServices(IServiceCollection services){}
		//运行时调用此方法。使用此方法配置HTTP请求管道。
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env){ }
    }
}
(3)发布服务配置:launchSettings

​ launchSettings是启动服务设置,对应了两种启动方式IIS Express和EFLearn.API,其中applicationUrl是相应启动方式对应的请求地址,launchUrl是默认启动api。

{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:22649",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "EFLearn.API": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}
(4)属性配置文件:appsettings

​ appsettings是项目属性配置文件,用于属性的配置。例如我们在原文件基础上新增连接字符串"ConnectionStrings"。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
  "ConnectionStrings": {
      "MySQL": "Server=localhost;Port=3306;Database=projectmanagement; User=root;Password=123456"
  }
}

2、三层架构

​ Web API分层架构可以分为:Models【实体层】,Controller【控制层】,Repositories【仓储层/数据层】,Services【服务层】。

二、Swagger的配置与使用

1、配置

(1)导Nuget包
Swashbuckle.AspNetCore
(2)注册服务

​ 我们需要在Startup中的ConfigureServices方法中添加相应的服务,这里通过AddSwaggerGen来进行注册配置。SwaggerDoc用于设置文档显示。

services.AddSwaggerGen(c =>
{
	c.SwaggerDoc("v1", new OpenApiInfo { Title = "EFLearn.API", Description="项目说明文档",Version = "v1" });
});
(2)配置服务

​ 我们需要在Startup中的Configure方法中配置相应的服务,这里配置了UseSwagger和UseSwaggerUI。

if (env.IsDevelopment())
{
	app.UseSwagger();
	app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "EFLearn.API v1"));
}

2、添加文档设置

​ 如果我们希望将如下Controller的接口注释添加到SwaggerUI显示中,我们需要先生成相应的文档,然后将生成的xml文档读取到Swagger中。

namespace EFLearn.API.Controllers
{
    /// <summary>
    /// 项目管理接口管理
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class ItemControController : ControllerBase
    {
        /// <summary>
        /// 获取项目名称
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string Get()
        {
            return "项目名称";
        }
    }
}
(1)生成文档设置

​ 在属性-输出中,设置XML文档文件路径,此处使用…/相对路径。

在这里插入图片描述

(2)配置swagger加载

​ AppContext.BaseDirectory为当前项目的相对路径,通过Path.Combine将相对路径名与文件名连接成完整的文件路径,随后通过IncludeXmlComments来加载注释文档,路径后的true参数是指该文档为controller文档。除了Controller以外,我们一般还需要为实体层建立swagger注释,方法类似。

services.AddSwaggerGen(c =>
{
	c.SwaggerDoc("v1", new OpenApiInfo { Title = "EFLearn.API", Description="项目说明文档",Version = "v1" });
    var xmlPath = Path.Combine(AppContext.BaseDirectory, "EFLearn.API.xml");
    c.IncludeXmlComments(xmlPath,true);
});
(5)取消无注释警告

​ 当我们配置了注释文档后,编译器会认为我们处处需要注释,我们可以通过增加1591字段来取消相应的显示警告。

在这里插入图片描述

三、EFCore连接MySQL

1、连接流程

​ 此处为基于.NET Core Web API 5进行MySQL的连接,需要在包含实体类和数据库上下文的包中导入以下两个Nuget包:

  • Microsoft.EntityFrameworkCore
  • Pomelo.EntityFrameworkCore.MySql
(1)建立数据库
use TestDb;
create table if not exists `item`(
	`id` int(4) not null auto_increment,
	`name` varchar(10) not null default "无名项目",
	primary key(`id`)
)ENGINE=INNODB  DEFAULT CHARSET=utf8;
(2)建立实体类
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Entity.Models
{
    [Table("item")]
    public class Item
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
    }
}
(3)建立数据库上下文
using Microsoft.EntityFrameworkCore;
using Entity.Models;

namespace Entity.DBContexts
{
    public class ItemDbContext : DbContext
    {
        public DbSet<Item> Items { get; set; }
        public ItemDbContext(DbContextOptions<ItemDbContext> options) : base(options){}
    }
}
(4)通过仓储类进行调用
using System.Collections.Generic;
using System.Linq;
using Entity.Models;
using Entity.DBContexts;
using Repositories.IRepositories;

namespace Repositories
{
    public class ItemRepositories : IItemRepositories
    {
        private readonly ItemDbContext _dbContext;
        public ItemRepositories(ItemDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public IEnumerable<Item> GetAll()
        {
            using (_dbContext)
            {
                return _dbContext.Items.ToList();
            }
        }
    }
}
(5)在appsettings.json增加字符串连接属性
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MySQL": "Server=localhost;Port=3306;Database=TestDb; User=root;Password=123456"
  }
}
(6)MySQL连接注册
services.AddDbContext<ItemDbContext>(options => options.UseMySql(Configuration.GetConnectionString("MySQL"), MySqlServerVersion.LatestSupportedServerVersion));
(7)注册依赖

​ 在类Startup中的ConfigureServices进行依赖的注册。

services.AddTransient<ItemRepositories>();

2、EF Core知识总结

​ 使用EFCore需要以下步骤:建立数据库表、构建EF模型[对应table]、定义数据库上下文[对应Database]。首先我们需要选择相应数据库建立相应的库表,然后在此基础上建立对应的EF模型。即实体类。最后我们需要定义数据库上下文(DbContext)进行数据库的操作。

四、依赖注入

1、依赖注入概念

​ IoC控制反转容器来控制对象的依赖注入,依赖注入就是指在程序运行过程中,一个对象需要使用到另一个对象的属性或方法时,不需要该对象去new另一个对象的实例,而是交给IoC来完成外部注入。.NET Core自带的依赖注入,只支持构造注入:构造注入能使得依赖变得更清晰,我既然依赖你,那么我实例化的时候你就必须得出现。

​ 但是.NET Core自带的注入方式,必须每次使用都要到ConfigureService方法中进行Add注册,若模块中涉及的类较多,则会导致代码冗杂,则推荐使用Autofac三方DI框架进行依赖注入实现。

2、内置DI容器

(1)构造注入使用

​ 先看一则案例:ItemService注入到ItemController。

ItemService:通过构造注入的方式注入了ItemRepositories

namespace Services
{
    public class ItemService : IItemService
    {
        private readonly IItemRepositories _repositories;
        public ItemService(IItemRepositories repositories)
        {
            _repositories = repositories;
        }

        public IEnumerable<Item> GetAllItem()
        {
          
           return _repositories.GetAll();
        }
    }
}

ItemController:通过构造注入的方式注入了ItemService

namespace EFCoreToMysql.API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ItemController : ControllerBase
    {

        private readonly IItemService _itemService;
        public ItemController(IItemService itemService)
        {
            _itemService = itemService;
        }


        [HttpGet]
        public IActionResult GetAll()
        {
            return Ok(_itemService.GetAllItem());
        }
    }
}

ConfigureServices(IServiceCollection services):我们通过接口声明进行注入时,需要提供相应接口,将接口和对应的类注册到容器中,若只提供了类,则只注册相应的类。

services.AddTransient<IItemService,ItemService>();
services.AddTransient<IItemRepositories,ItemRepositories>();
//只提供类
//services.AddTransient<ItemDbContext>();
(2)生命周期与作用域

​ 使用.NET Core集成的IoC容器,只需要定义好相应的接口,在Startup.cs的ConfigureService方法里使用对应的生命周期的绑定方法即可,常见的方法如下:

services.AddTransient<IApplicationService,ApplicationService>();
service.AddScoped<IApplicationService,ApplicationService>();
service.AddSingleton<IApplicationService,ApplicationService>();

//但遇到泛型基类注册时
service.AddScoped(typeof(Ixxx<>),typeof(xxx<>));

使用不同的方法对应了注入的不同生命周期:

  • Transient:服务在每次请求时被创建,每一次请求获取得到的都是不同的对象,最好用于轻量级无状态服务,比如Repository
  • Scoped:服务在每次请求时被创建,直到请求结束,在一次请求过程中所获取的都是同一个对象
  • Singleton:项目启动直至项目关闭整个周期只会创建一次对象,相当于静态类

对应了Microsoft.Extensions.DependencyInjection.ServiceLifetime的三个枚举值:

public enum ServiceLifetime
{
  Singleton,
  Scoped,
  Transient
}

3、知识补充

(1)DI相关类

​ 我们都是在ConfigureServices(IServiceCollection services)进行服务的注册,该接口的定义如下,继承了IList,泛型定义为ServiceDescriptor,所以IServiceCollection本质就是IList。

using System.Collections.Generic;
namespace Microsoft.Extensions.DependencyInjection
{
    public interface IServiceCollection : IList<ServiceDescriptor>{}
}

​ 而AddScoped、AddTransient、AddSingleton的方法位于ServiceCollectionServiceExtensions扩展类,用作IServiceCollection的扩展方法。

namespace Microsoft.Extensions.DependencyInjection
{
    //Extension methods for adding services to an <see cref="IServiceCollection" />.
    public static class ServiceCollectionServiceExtensions
(2)扩展方法

​ 知识点插入:当我们使用sealed关键字修饰类时就不能进行继承,这是为了防止该类的行为被破坏。但是我们可以通过两个方式来进行功能的重用:静态方法和扩展方法。静态方式是将该类型作为参数传入,从而实现重用。使用扩展方法,能够减少输入的代码量,扩展方法是静态方法的拓展。

​ 比如我们想要扩展string类型的操作,增加检查字符串是否为电子邮箱格式,相应的静态方法和扩展方法如下。

//静态方法扩展
public class StringExtensions{
    public static bool IsValidEmail(string input){
        return Regex.IsMatch(input,@"[a-zA-Z0-9\.-_]+@[a-zA-Z0-9\.-_]+");
    }
}
//扩展方法
public class StringExtensions{
    public static bool IsValidEmail(this string input){
        return Regex.IsMatch(input,@"[a-zA-Z0-9\.-_]+@[a-zA-Z0-9\.-_]+");
    }
}

​ 扩展方法即是在静态方法的基础上,在传入参数前增加this关键字,将其变为String类型的扩展方法,我们可以通过string.IsValidEmail直接进行调用。

(3)ServiceCollection简析

​ 在.Net Core里提供了默认的依赖注入容器IServiceCollection,它是一个轻量级容器。核心组件为两个IServiceCollection和IServiceProvider,IServiceCollection负责注册,IServiceProvider负责提供实例。

​ 我们看下方ServiceConllection源码,因为是对IList<ServiceDescriptor>的具体实现,所以很像一个集合类(就是)。其实现的方法具体依靠List<ServiceDescriptor> _descriptors来完成,则其核心为ServiceDescriptor。

namespace Microsoft.Extensions.DependencyInjection
{
    /// Default implementation of <see cref="IServiceCollection"/>.
    public class ServiceCollection : IServiceCollection
    {
        private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
        public int Count => _descriptors.Count;
        public bool IsReadOnly => false;
       
    }
}

​ 使用官方DI时注册我们都是将服务注册到一个ServiceCollection对象中,IServiceCollection集合就是一个继承IList<ServiceDescriptor>集合接口的一个类型,而ServiceDescriptor类型则是一个注册的服务描述类型,我们传入注册最后都会封装为一个ServiceDescriptor类型然后缓存到ServiceCollection集合之中。

3、三方组件:Autofac

(1)准备

​ 在使用之前我们先添加一个类库Extensions,然后加入以下NuGet依赖包:Autofac.Extensions.Dependencylnjection和Autofac.Extras.DynamicProxy。然后我们在Extensions类库中引入Services类库,在Controller类库取消Services类库的引用,改为引用Extensions类库。

在这里插入图片描述

(2)注入注册类:AutofacModuleRegister

​ 定义一个类AutofacMoudleRegister实现Autofac.Module,然后重写Load方法,通过builder.RegisterType<实现类名>().As<接口名>();进行注册。

using Autofac;
using Services.IService;
using Services;

namespace Extensions.ServiceExtensions
{
    public class AutofacMoudleRegister : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<ItemService>().As<IItemService>();
            //泛型注册
            //builder.RegisterType(typeof(xxx<>)).As(typeof(Ixxx<>)).InstancePerDependency();
        }
    }
}
(3)容器配置

​ 首先需要在CreateHostBuilder方法中,通过Host.UseServiceProviderFactory,传入AutofacServiceProviderFactory实例进行相应的容器添加(Use)。

namespace EFCoreToMysql.API
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            	//配置Autofac工厂容器
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

​ 然后在Startup类中增加相应的容器配置方法:

public void ConfigureContainer(ContainerBuilder builder)
{
	builder.RegisterModule<AutofacModuleRegister>();
}
(4)批量注入

​ 我们发现不仅要到Program和StartUp进行相应的注册配置,还得在Autofac.Module的实现类中通过builder.RegisterType.As这样的方式将类进行注册,才能使用依赖注入。这并没有减轻我们的工作量,但是除此方式以外,Autofac还增加了程序集批量注入的方式。

​ 我们通过反射的方式通过Assembly.Load【Load(AssemblyName) 在给定程序集的 AssemblyName的情况下,加载程序集】将程序集中的类引入,然后通过builder.RegisterAssemblyTypes(程序集).AsImplementedInterfaces()来进行依赖注册。

using Autofac;
using Services.IService;
using Services;
using System.Reflection;

namespace Extensions.ServiceExtensions
{
    public class AutofacModuleRegister : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            //Services注入
            var assemblysServices = Assembly.Load("Services");
            builder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces();

            //Repositories注入
            var assemblysRepositories = Assembly.Load("Repositories");
            builder.RegisterAssemblyTypes(assemblysRepositories).AsImplementedInterfaces();
        }
    }
}

五、PowerDesigner数据库设计

1、新建项目

​ 新建项目,这里可根据数据库设计直接建立PDM,

在这里插入图片描述

2、创建表格实体

​ 通过Palette进行实体的创建,和外键联系的建立。

在这里插入图片描述

3、生成SQL文件

​ 在[数据库-生成数据库]或Ctrl+G来生成相应的SQL文件。

在这里插入图片描述

六、Web API的增删改查

1、Entity层

Project

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Entity.Models
{
    [Table("project")]
    public class Project
    {
        [Key]
        public int PId { get; set; }
        public string PName { get; set; }
        public int Synopsis { get; set; }
        public string PType { get; set; }
        public int Star { get; set; }
        public int Is_Filed { get; set; }
        public int Is_Del { get; set; }
        public int Del_Time { get; set; }
    }
}

2、Repositories层

ProjectDbContext

using Microsoft.EntityFrameworkCore;
using Entity.Models;

namespace Repository.DbContexts
{
    public class ProjectDbContext : DbContext
    {
        public DbSet<Project> Projects { get; set; }
        public ProjectDbContext(DbContextOptions<ProjectDbContext> options) : base(options) { }
    }
}

ProjectRepository

using System.Collections.Generic;
using System.Linq;
using Repository.DbContexts;
using Entity.Models;
using Repository.IRepository;

namespace Repository
{
    public class ProjectRepository : IProjectRepository
    {
        private readonly ProjectDbContext _dbContext;
        public ProjectRepository(ProjectDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public void Add(Project project)
        {
            _dbContext.Projects.Add(project);
            _dbContext.SaveChanges();
        }

        public void Delete(Project project)
        {
            var projects = from p in _dbContext.Projects
                           where p.PId == project.PId
                           select p;
            if (projects.FirstOrDefault() is Project)
            {
                _dbContext.Projects.Remove(projects.FirstOrDefault());
                _dbContext.SaveChanges();
            }
        }

        public IEnumerable<Project> GetAll()
        {
            return _dbContext.Projects.ToList();
        }

        public Project GetById(int  id)
        {
            var projects = from project in _dbContext.Projects
                           where project.PId == id
                           select project;
            if(projects.FirstOrDefault() is Project)
            {
                return projects.FirstOrDefault();
            }
            return null;
        }

        public void Update(Project project)
        {
            var projects = from p in _dbContext.Projects
                           where p.PId == project.PId
                           select p;
            if(projects.FirstOrDefault() is Project)
            {
                Project newproject = projects.FirstOrDefault();
                newproject.PType = project.PType;
                newproject.PName = project.PName;
                newproject.Synopsis = project.Synopsis;
                _dbContext.SaveChanges();
            }
        }
    }
}

3、Services层

ProjectService

using Entity.Models;
using Service.IService;
using System.Collections.Generic;
using Repository.IRepository;

namespace Service
{
    public class ProjectService : IProjectService
    {
        private readonly IProjectRepository _repository;

        public ProjectService(IProjectRepository repository)
        {
            _repository = repository;
        }

        public IEnumerable<Project> GetAll()
        {
            return _repository.GetAll();
        }

        void IProjectService.Add(Project project)
        {
            _repository.Add(project);
        }

        void IProjectService.Delete(Project project)
        {
            _repository.Delete(project);
        }

        Project IProjectService.GetById(int id)
        {
            return _repository.GetById(id);
        }

        void IProjectService.Update(Project project)
        {
            _repository.Update(project);
        }
    }
}

4、Controllers层

ProjectController

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Entity.Models;
using Service.IService;
using System.Collections.Generic;


namespace ProjectManagement.API.Controllers
{
    /// <summary>
    /// 项目接口
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class ProjectController : ControllerBase
    {

        private readonly IProjectService _projectService;

        public ProjectController(IProjectService projectService)
        {
            _projectService = projectService;
        }

        [HttpGet]
        [Route("getAllProject")]
        public IEnumerable<Project>  GetAllProjects()
        {
            return _projectService.GetAll();
        }

        [HttpGet]
        [Route("getProjectByID")]
        public  Project GetProjectByID(int id)
        {
            return _projectService.GetById(id);
        }

        [HttpPost]
        [Route("addProject")]
        public void AddProject(Project project)
        {
            _projectService.Add(project);
        }

        [HttpDelete]
        [Route("delProject")]
        public void DelProject(Project project)
        {
            _projectService.Delete(project);
        }

        [HttpPost]
        [Route("updateProject")]
        public void UpdateProject(Project project)
        {
            _projectService.Update(project);
        }
    }
}

七、GitLab与Visual Studio使用Git

1、生成SSH

​ 在Git\usr\bin目录下打开命令提示符,输入ssh-keygen -t rsa -C “GitLab的邮箱地址”,生成相应的SSH文件[C:\Users\用户名\.ssh\id_ras.pub]。

在这里插入图片描述

​ 然后到GitLab的用户设置中的SSH密钥进行添加,将id_ras.pub输入到相应位置,点击添加即可。

在这里插入图片描述

2、Visual Studio与远程库的使用

(1)创建远程库

在这里插入图片描述

(2)先拉取

在这里插入图片描述

(3)再推送

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未来村村长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值