您好,欢迎来到爱go旅游网。
搜索
您的当前位置:首页.Netcore搭建仓储模式+SqlsugarORM+AutoFac

.Netcore搭建仓储模式+SqlsugarORM+AutoFac

来源:爱go旅游网
.Netcore搭建仓储模式+SqlsugarORM+AutoFac

⼀、为什么使⽤仓储模式

个⼈觉得仓储模式就是升级版的三层架构,⾥⾯多了接⼝之间的调⽤,就好像仓库管理员,只是负责把仓库⾥⾯的东西交给客户,也⽆需知道客户要做什么,反之客户也⽆需知道东西在哪⾥,怎么存放的,需要的时候就跟管理员索要就可以了,这样就是低耦合,就算管理员换⼈了,客户的流程也⽆需做出改变,反之亦然。实际做⼀遍就⼀⽬了然了。第⼀步:先把类库创建好

Common类库,公共类库,放⼀些公共的类和⽅法,⽐如,Helper类 字符串转换,编码格式这些。IRepositry、Repository仓储层,负责处理数据 就是ADO。net那套东西。IService、Service 业务服务层,三层架构的BLL。Model 存放实体类

仓储层,创建ITestRepository和TestRepository,这⾥我们先不连接数据库,下⾯讲到ORM的时候在连库。然后在服务层,创建ITestService和TestService类

代码如下,最好不要复制粘贴,⾃⼰⼀个⼀个创建,这样能更好的了解,哪个类库是做什么的,要怎么做

using System;

namespace WebApi.Core.IRepository{

public interface ITestRepository {

///

/// 求和,这⾥就是定义⼀下具体实现在 TestRepository ///

/// /// /// int Sum(int i, int j); }}

using System;

using WebApi.Core.IRepository;

namespace WebApi.Core.Repository{

///

/// 仓储具体实现类,继承仓储接⼝类 ///

public class TestRepository:ITestRepository {

///

/// 求和,仓储实现,这个过程就是访问数据库得到数据,并且返回 ///

/// /// /// public int Sum(int i, int j) {

return i + j; } }}

using System;

namespace WebApi.Core.IService{

///

/// 业务服务接⼝层 ///

public interface ITestService {

///

/// 求和声明 ///

/// /// /// int SumService(int i, int j); }}

using System;

using WebApi.Core.IRepository;using WebApi.Core.IService;using WebApi.Core.Repository;

namespace WebApi.Core.Service{

public class TestService:ITestService {

//这⾥如果要⽤到仓储,需要先创建对象,就好像要知道管理员是谁,这个先这么写,后期改成AutoFac的 ITestRepository test = new TestRepository(); ///

/// 求和,调⽤仓储的求和⽅法,把参数传递给仓储 ///

/// /// ///

public int SumService(int i, int j) {

return test.Sum(i,j); } }}

好了⼀个简单的仓储模式算是搭建完成了,接下来我们测试⼀下,创建⼀个Controller名字随便取,在⾥⾯添加如下代码

///

/// 测试仓储模式,求和表⽰层 ///

/// /// /// [HttpPost]

public int SumService(int i, int j) {

//引⽤service层

ITestService testService = new TestService(); return testService.SumService(i,j); }

F5启动⼀下,我们测试⼀下,从测试结果看,是没有任何问题的

接下来,我们在仓储模式下增加IOC,上⾯写的时候会发现⼀些new 实体的 代码,这样的耦合性就很强了,所以需要使⽤IOC模式来处理这种耦合,这⾥我们⽤.net架构⽐较常⽤的Autofac

Nuget包引⼊两个 Autofac.Extras.DynamicProxy、Autofac.Extensions.DependencyInjection新建⼀个类来存放Autofac注册Module,如下代码

using Autofac;

using Autofac.Extras.DynamicProxy;using System;

using System.Collections.Generic;using System.Linq;

using System.Reflection;

using System.Threading.Tasks;

namespace WebApi.Core.Api.SetUpService{

///

/// Autofac注册服务类 ///

public class AutofacModuleRegister:Autofac.Module {

///

/// 重写Load函数 ///

///

protected override void Load(ContainerBuilder builder) {

//注册Service

var assemblysServices = Assembly.Load(\"Webapi.Core.Service\"); builder.RegisterAssemblyTypes(assemblysServices)

.InstancePerDependency()//默认模式,每次调⽤,都会重新实例化对象;每次请求都创建⼀个新的对象

.AsImplementedInterfaces()//是以接⼝⽅式进⾏注⼊,注⼊这些类的所有的公共接⼝作为服务(除了释放资源) .EnableInterfaceInterceptors(); //引⽤Autofac.Extras.DynamicProxy;应⽤

//注册Repository

var assemblysRepository = Assembly.Load(\"Webapi.Core.Repository\"); builder.RegisterAssemblyTypes(assemblysRepository)

.InstancePerDependency()//默认模式,每次调⽤,都会重新实例化对象;每次请求都创建⼀个新的对象

.AsImplementedInterfaces()//是以接⼝⽅式进⾏注⼊,注⼊这些类的所有的公共接⼝作为服务(除了释放资源)

.EnableInterfaceInterceptors(); //引⽤Autofac.Extras.DynamicProxy;应⽤ } }}

在startup.cs⽂件中,增加⼀个⽅法,⽤来配置Autofac服务容器,在Configure⽅法下⾯,新建如下代码

///

/// 注册Autofac容器 ///

///

public void ConfigureContainer(ContainerBuilder builder) {

builder.RegisterModule(new AutofacModuleRegister()); }

在program.cs ⽂件中 初始化⼀下实例代码如下

public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args)

.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //这⾥是替换容器的,微软默认的注⼊⽅式是DI,替换成autofac实例 .ConfigureWebHostDefaults(webBuilder => {

webBuilder.UseStartup(); });

到此 我们的注册就算完成了,接下来我们使⽤⼀下,如下图,我们上⾯练习仓储的时候,new 了⼀个仓储的实例,现在我们来改变⼀下

改成如下代码

public class TestService:ITestService {

//这⾥如果要⽤到仓储,需要先创建对象,就好像要知道管理员是谁,这个先这么写,后期改成AutoFac的 //ITestRepository test = new TestRepository(); //改造成Autofac注⼊⽅式,声明⼀个仓储常量 private readonly ITestRepository test; //构造函数注⼊

public TestService(ITestRepository testRepository) {

test = testRepository; }

///

/// 求和,调⽤仓储的求和⽅法,把参数传递给仓储 ///

/// /// ///

public int SumService(int i, int j) {

return test.Sum(i,j); } }

testController这⾥也有⼀个new 也需要变化⼀下

改成如下代码

///

/// 测试仓储模式控件 ///

public class TestRepositoryController : BaseController {

//声明⼀个常量

private readonly ITestService testService; //构造函数注⼊ service

public TestRepositoryController(ITestService testS) {

testService = testS; }

///

/// 测试仓储模式,求和表⽰层 ///

/// /// /// [HttpPost]

public int SumService(int i, int j) {

return testService.SumService(i,j); } }

接下来我们F5启动项⽬,测试⼀下,注⼊是否已完成,是否会报错。从结果上看⽊有问题啊,哎真希望报错,踩踩坑啊。这也太顺利了

还有⼀个问题,就是如果⼀个接⼝被好多服务继承了,依赖注⼊会怎么处理,会注⼊哪个服务呢?我们来测试⼀下⾸先在ITestService ⾥⾯在添加⼀个⽅法

///

/// 返回⽀付类型 ///

/// string payType();

然后在创建⼀个服务来继承ITestService接⼝

using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IService;

namespace WebApi.Core.Service{

public class TestPayService:ITestService {

    ///

    /// 测试⼀个接⼝多个实例下的依赖注⼊    ///

    /// public string payType()

{

return \"微信\"; }

public int SumService(int i, int j) {

return 1; } }}

在TestService也要加⼀个⽅法

///

/// 测试⼀个接⼝多个实例下的依赖注⼊ ///

/// public string payType() {

return \"⽀付宝\"; }

在TestRepositoryController 下添加⼀个接⼝

///

/// 测试⼀个接⼝多个实例下的依赖注⼊ ///

/// [HttpGet]

public string payType() {

return testService.payType(); }

按F5我们看⼀下结果,可以看到返回的始终是 ⽀付宝,不管调⽤多少次,如果我想要微信怎么办呢,或者两个都显⽰。

其实很简单的,需要在startup.cs 的ConfigureServices 加⼀段代码

public void ConfigureServices(IServiceCollection services) {

//注册AppSettings读取类

services.AddSingleton(new AppSettings(Configuration)); //注册Swagger服务

services.AddSwaggerSetup(); //jwt授权验证

services.AddAuthorizationJwtSetUp();

services.AddControllers();

//同⼀个接⼝两个实现,依赖注⼊

services.AddTransient(); services.AddTransient(); }

然后在TestRepositoryController的构造函数修改⼀下

using System;

using System.Collections.Generic;using System.Linq;

using System.Threading.Tasks;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Mvc;using Newtonsoft.Json.Linq;using WebApi.Core.IService;using WebApi.Core.Service;

namespace WebApi.Core.Api.Controllers{

///

/// 测试仓储模式控件 ///

public class TestRepositoryController : BaseController {

//声明⼀个常量

private readonly ITestService testService; private readonly ITestService testPayService; //构造函数注⼊ service 这⾥使⽤的就是⼀对多注⼊

public TestRepositoryController(IEnumerable testS) {

testService = testS.FirstOrDefault(x => x.GetType().Name == \"TestService\");

testPayService = testS.FirstOrDefault(x=>x.GetType().Name == \"TestPayService\"); }

///

/// 测试仓储模式,求和表⽰层 ///

/// /// /// [HttpPost]

public int SumService(int i, int j) {

return testService.SumService(i,j); }

///

/// 测试⼀个接⼝多个实例下的依赖注⼊ ///

/// [HttpGet]

public string payType() {

return testService.payType()+\";\"+testPayService.payType(); } }}

然后我们运⾏F5看⼀下结果,是没问题的,当然这种写法,会根据业务需求来定,我个⼈觉得不会经常出现,如果遇到了 会⽤就⾏。

还有⼀个属性注⼊,但是.NET如果需要⽤属性注⼊的话,属性就需要public暴露出去,所以推荐使⽤构造函数注⼊。下⾯我们继续 Sqlsugar + 改造⼀下仓储,变成异步的。我们这边⽤的数据是oracle,⾸先创建⼀个表 名字随便起⼀个

我们在Repository 和 model层中引⼊ Nuget包 sqlSugarCore然后修改⼀下UserModel 代码如下

using SqlSugar;using System;

namespace WebApi.Core.Model{

///

/// ⽤户实体类 注意这个SugarTable要改成你⾃⼰的表名字不然会报错哦 ///

[SugarTable(\"testcoreuser\")] public class UsersModel {

///

/// id

///

[SugarColumn(IsPrimaryKey = true, IsIdentity = true)] public int UserId { get; set; } ///

/// 姓名

///

public string UserName { get; set; } ///

/// 年龄

///

public int? Age { get; set; } }}

我们在Repository层中新建⼀个DbContext类,和BaseDBConfig 类 代码如下

using SqlSugar;using System;

using System.Collections.Generic;using System.Linq;

using System.Linq.Expressions;using WebApi.Core.Model;

namespace WebApi.Core.Repository.SqlSugar{

public class DbContext where T : class, new(){

public DbContext() {

Db = new SqlSugarClient(new ConnectionConfig() {

ConnectionString = BaseDBConfig.ConnectionString, DbType = DbType.SqlServer,

InitKeyType = InitKeyType.Attribute,//从特性读取主键和⾃增列信息 IsAutoCloseConnection = true,//开启⾃动释放模式

});

//调式代码 ⽤来打印SQL

Db.Aop.OnLogExecuting = (sql, pars) => {

Console.WriteLine(sql + \"\\r\\n\" +

Db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); Console.WriteLine(); }; }

//注意:不能写成静态的

public SqlSugarClient Db;//⽤来处理事务多表查询和复杂的操作

public SimpleClient CurrentDb { get { return new SimpleClient(Db); } }//⽤来操作当前表的数据

public SimpleClient UserDb { get { return new SimpleClient(Db); } }//⽤来处理User表的常⽤操作}}

using System;

using System.Collections.Generic;using System.Text;

namespace WebApi.Core.Repository.SqlSugar{

public class BaseDBConfig {

///

/// 数据库连接字符串oracle ///

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

然后在 startup.cs 的ConfigureServices() ⽅法中读取 appsettings.json 配置的数据

接下来我们优化⼀下仓储模式,先创建⼀个仓储基类 IBaseRepository.cs 代码如下

using System;

using System.Collections.Generic;using System.Text;

using System.Threading.Tasks;

namespace WebApi.Core.IRepository.Base{

///

/// 基类接⼝,其他接⼝继承该接⼝ ///

///

public interface IBaseRepository where TEntity : class {

///

/// 根据ID查询 ///

/// ///

Task QueryByID(object objId); ///

/// 添加

///

/// ///

Task Add(TEntity model); ///

/// 修改

///

/// ///

Task Update(TEntity model); ///

/// 删除

///

/// ///

Task DeleteByIds(object[] ids);

}}

在创建IUserRepository 然后继承IBaseRepository 代码如下

using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IRepository.Base;using WebApi.Core.Model;

namespace WebApi.Core.IRepository{

public interface IUserRepository:IBaseRepository { }}

同样的操作,需要在Repository做⼀遍

创建⼀个BaseRepository和UserRepository代码如下,异步的不要随便加Task.Run() 会有意想不到的问题。会有⼀些⼈不知道where 后⾯是做什么的,其实就是对泛型的约束

class 是表⽰传过来的 必须是类,new()表⽰ 类型参数必须有⼀个公有的,⽆参数的构造⽅法.当和别的约束⼀起使⽤的时候,new()约束必须放在最后

using System;

using System.Collections.Generic;using System.Text;

using System.Threading.Tasks;

using WebApi.Core.IRepository.Base;using WebApi.Core.Repository.SqlSugar;

namespace WebApi.Core.Repository.Base{

///

/// 基类实现 ///

///

public class BaseRepository : DbContext, IBaseRepository where TEntity : class, new() {

///

/// 写⼊实体数据 ///

/// ///

public async Task Add(TEntity model) {

//这⾥需要注意的是,如果使⽤了Task.Run()就会导致 sql语句⽇志⽆法记录改成下⾯的 //var i = await Task.Run(() => Db.Insertable(model).ExecuteCommand()); var i = await Db.Insertable(model).ExecuteCommandAsync(); return i>0; }

///

/// 根据ID删除 ///

/// ///

public async Task DeleteByIds(object[] ids) {

var i = await Db.Deleteable().In(ids).ExecuteCommandAsync(); return i > 0; }

///

/// 根据ID查询⼀条数据 ///

/// ///

public async Task QueryByID(object objId) {

return await Db.Queryable().InSingleAsync(objId); }

///

/// 更新实体数据

///

/// ///

public async Task Update(TEntity model) {

//这种⽅式会以主键为条件

var i = await Db.Updateable(model).ExecuteCommandAsync(); return i > 0; } }}

using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IRepository;using WebApi.Core.Model;

using WebApi.Core.Repository.Base;

namespace WebApi.Core.Repository{

public class UserRepository:BaseRepository,IUserRepository { }}

仓储层都有了 基类,那么service怎么可以没有呢,跟仓储差不多

4个类的代码就全部放在⼀个⾥⾯了,如果要复制,别全部复制,要分批,还是不建议复制粘贴

using System;

using System.Collections.Generic;using System.Text;

using System.Threading.Tasks;

namespace WebApi.Core.IService.Base{

public interface IBaseService where TEntity:class {

///

/// 根据ID列表删除 ///

/// ///

Task DeleteByIds(object[] ids); ///

/// 根据ID查询 ///

/// ///

Task QueryByID(object objId); ///

/// 添加实体 ///

/// ///

Task Add(TEntity model); ///

/// 更新实体 ///

/// ///

Task Update(TEntity model); }}

using System;

using System.Collections.Generic;using System.Text;

using System.Threading.Tasks;

using WebApi.Core.IRepository.Base;

using WebApi.Core.IService.Base;using WebApi.Core.Repository.Base;

namespace WebApi.Core.Service.Base{

///

/// 服务基类 ///

///

public class BaseService : IBaseService where TEntity : class, new() {

      private readonly IBaseRepository baseDal;

      //这⾥使⽤依赖注⼊

      public BaseService(IBaseRepository baseRepository)      {

        baseDal = baseRepository;      }

///

/// 写⼊实体 ///

/// ///

public async Task Add(TEntity model) {

return await baseDal.Add(model); }

///

/// 根据ID删除 ///

/// ///

public async Task DeleteByIds(object[] ids) {

return await baseDal.DeleteByIds(ids); }

///

/// 根据ID查询 ///

/// ///

public async Task QueryByID(object objId) {

return await baseDal.QueryByID(objId); }

///

/// 更新实体 ///

/// ///

public async Task Update(TEntity model) {

return await baseDal.Update(model); } }}

using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IService.Base;using WebApi.Core.Model;

namespace WebApi.Core.IService{

public interface IUserService:IBaseService { }}

using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IService;using WebApi.Core.Model;

using WebApi.Core.Service.Base;

namespace WebApi.Core.Service{

public class UserService:BaseService,IUserService {

     //这⾥我们要把实参传到baseService 不然会报错

      public UserService(IBaseRepository baseRepository):base(baseRepository)      {      }

}}

接下来 我们测试⼀下,新增⼀个UserTest控制器代码如下

using System;

using System.Collections.Generic;using System.Linq;

using System.Threading.Tasks;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Mvc;using WebApi.Core.IService;using WebApi.Core.Model;

namespace WebApi.Core.Api.Controllers{

///

/// ⽤户表测试ORMSqlSuggr ///

public class UserORMController : BaseController {

//声明⼀个常量

private readonly IUserService userService; ///

/// 通过构造函数依赖注⼊ ///

///

public UserORMController(IUserService userSer) {

userService = userSer; }

///

/// 根据id获取数据 ///

/// 参数id /// [HttpPost]

public async Task GetUser(int id) {

UsersModel user = await userService.QueryByID(id); return Ok(user); }

///

/// 添加数据 ///

/// 参数id /// [HttpPost]

public async Task Add(UsersModel user) {

var count = await userService.Add(user); return Ok(count); }

///

/// 更新数据 ///

/// 参数id /// [HttpPost]

public async Task Update(UsersModel user) {

var sucess = await userService.Update(user); return Ok(sucess); }

///

/// 删除数据 ///

/// 参数id /// [HttpPost]

public async Task Delete(object[] ids) {

var sucess = await userService.DeleteByIds(ids); return Ok(sucess); } }}

终于完⼯了,我们来F5运⾏看⼀下,数据已经有了,剩下的各位可以试⼀下,建议可以⾃⼰试着改⼀下看看各种效果

删除可以批量传⼊ID

可以看到 sql语句也打印出来了,是在DBContext ⾥⾯ 使⽤ Db.Aop.OnLogExecuting 这句话是 sql执⾏同时记录

接下来我们把SqlSugar下的常⽤查询⽅法试⼀下(有太多了,需要⽤到的在去搜索⼀下就可以,万变不离其宗,就是lambda表达式和⼀些内置函数的⽀持,跳转到源码估计也就是ADO.net那些东西)

在仓储层增加⼀个⽅法,IUserRepository和UserRepository ,服务层 IUserService 和UserService 也需要添加,然后在Controller中添加⼀个接⼝调⽤

这⾥也是把东西放在⼀起了,其中UserRepository重点看⼀下

using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IRepository.Base;using WebApi.Core.Model;

namespace WebApi.Core.IRepository{

public interface IUserRepository:IBaseRepository {

///

/// 测试sqlSugar 常⽤语句 ///

void testSqlSugar(); }}

using Newtonsoft.Json;using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IRepository;using WebApi.Core.Model;

using WebApi.Core.Repository.Base;

namespace WebApi.Core.Repository{

public class UserRepository:BaseRepository,IUserRepository {

///

/// 测试sqlSugar 常⽤语句 ///

public void testSqlSugar() {

//查询⼀个集合

List list = Db.Queryable().ToList(); //把list转换成json串打印输出

Console.WriteLine(JsonConvert.SerializeObject(list)); //打印集合的数量

Console.WriteLine(list.Count);

//直接查询并转换成json 跟上⾯的输出结果⼀样,没有任何区别 var testJson = Db.Queryable().ToJson(); Console.WriteLine(testJson); //根据条件查询单条数据

UsersModel usermodel = Db.Queryable().Single(t=>t.Age==30); //判断不为空,打印其中⼀个字段值

Console.WriteLine(usermodel?.UserName +\"----------\"); //根据主键查询单条数据

usermodel = Db.Queryable().InSingle(1); //判断不为空,打印其中⼀个字段值

Console.WriteLine(usermodel?.UserName + \"----------\"); //查询姓名为张三的第⼀条记录

usermodel = Db.Queryable().Where(t => t.UserName == \"张三\").First(); Console.WriteLine(usermodel?.UserName + \"----------\"); //判断数据是否存在

bool exists = Db.Queryable().Any(t=>t.UserName==\"⼩兰\"); Console.WriteLine(exists.ToString()); //模糊查询

testJson = Db.Queryable().Where(t => t.UserName.Contains(\"李\")).ToJson(); Console.WriteLine(testJson); //以xx结尾 或开始

testJson = Db.Queryable().Where(t => t.UserName.EndsWith(\"三\") || t.UserName.StartsWith(\"李\")).ToJson(); Console.WriteLine(testJson); } }}

using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IService.Base;using WebApi.Core.Model;

namespace WebApi.Core.IService{

public interface IUserService:IBaseService {

///

/// 测试sqlSugar 常⽤语句 ///

void testSqlSugar(); }}

using System;

using System.Collections.Generic;using System.Text;

using WebApi.Core.IRepository;

using WebApi.Core.IRepository.Base;using WebApi.Core.IService;using WebApi.Core.Model;

using WebApi.Core.Service.Base;

namespace WebApi.Core.Service{

public class UserService:BaseService,IUserService {

//声明常量

private readonly IUserRepository userService;

public UserService(IBaseRepository baseRepository, IUserRepository usersSer):base(baseRepository) {

userService = usersSer; }

///

/// 测试sqlSugar 常⽤语句 ///

public void testSqlSugar() {

userService.testSqlSugar(); } }}

using System;

using System.Collections.Generic;using System.Linq;

using System.Threading.Tasks;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Mvc;using WebApi.Core.IService;using WebApi.Core.Model;

namespace WebApi.Core.Api.Controllers{

///

/// ⽤户表测试ORMSqlSuggr ///

public class UserORMController : BaseController {

//声明⼀个常量

private readonly IUserService userService; ///

/// 通过构造函数依赖注⼊ ///

///

public UserORMController(IUserService userSer) {

userService = userSer; }

///

/// 根据id获取数据 ///

/// 参数id /// [HttpPost]

public async Task GetUser(int id) {

UsersModel user = await userService.QueryByID(id); return Ok(user); }

///

/// 添加数据 ///

/// 参数id /// [HttpPost]

public async Task Add(UsersModel user) {

var count = await userService.Add(user); return Ok(count); }

///

/// 更新数据 ///

/// 参数id /// [HttpPost]

public async Task Update(UsersModel user) {

var sucess = await userService.Update(user); return Ok(sucess); }

///

/// 删除数据 ///

/// 参数id /// [HttpPost]

public async Task Delete(object[] ids) {

var sucess = await userService.DeleteByIds(ids); return Ok(sucess); }

///

/// 测试sqlSugar 常⽤语句 ///

/// [HttpGet]

public IActionResult testSqlSugar() {

userService.testSqlSugar(); return Ok(\"true\"); } }}

这⾥代码问题解决后,F5启动看看结果

运⾏没问题,没有报错,然后我们看⼀下控制台输出,可以看到输出的sql语句和 打印出的结果,这⾥可以跟代码⽐对⼀下。也可以⾃⼰搜索⼀些分页啊等等的,可以看⼀下结果

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务