手动从0搭建ABP框架-ABP官方完整解决方案和手动搭建简化解决方案实践

  本文主要讲解了如何把ABP官方的在线生成解决方案运行起来,并说明了解决方案中项目间的依赖关系。然后手动实践了如何从0搭建了一个简化的解决方案。ABP官方的在线生成解决方案源码下载参考[3],手动搭建的简化的解决方案源码下载参考[4]。

一.ABP官方在线生成解决方案

1.将在线生成解决方案跑起来

首先进入页面https://abp.io/get-started,然后创建项目:641601958fb9e92b445185bf76143442.png然后头脑中要有一个项目之间的依赖关系图,不清楚的可以参考《基于ABP实现DDD》:10d56140f30f46734df0899aa74a88ac.png

截止目前为止,项目使用的.NET版本是6.0,ABP版本是5.3.3。使用Rider打开项目Acme.BookStore后,会提示使用yarn安装package,安装包后:2488effbaba436982594cb20a13070c4.png在整个解决方案中搜索ConnectionStrings,发现其在Acme.BookStore.HttpApi.Host、Acme.BookStore.DbMigrator和Acme.BookStore.IdentityServer这3个启动项目中出现:8329a15ef76ec3dc97e589499f4f36c7.png将ConnectionStrings中的内容替换为:

"ConnectionStrings": {"Default": "Server=127.0.0.1;Database=BookStore;Trusted_Connection=True;User ID=sa;Password=913292836;"},

然后开始运行Acme.BookStore.DbMigrator进行数据种子迁移:e129768a6b67aa1dce45971868bdfcad.png出现上面图片输出结果,基本上表示迁移成功完成了,接下来看看数据库:096591ae543856d7be0a8df1d2db1abd.png运行Acme.BookStore.Web项目如下:

d3c9d5dd96e40343ff8e5458c712bc13.png启动后发现报错了,发现主要是3个问题:一个是Redis没有启动,另一个问题是IDS4服务没有启动,最后一个问题是Acme.BookStore.HttpApi.Host没有启动a3f9d8fa8e17a8c93b9976cc4a5574d3.png启动IDS4服务的时候发现报错Volo.Abp.AbpException: Could not find the bundle file '/libs/abp/core/abp.css' for the bundle 'Basic.Global'!

328b0832a5d33505076e81ef9761b4c0.png在该项目下执行命令abp install-libs:622f2c9e13c12669dd507d1d0c563e0d.png发现消息提示说ABP CLI有个更新的5.3.3版本,通过命令dotnet tool update -g Volo.Abp.Cli进行升级。再次运行Acme.BookStore.IdentityServer项目,发现不报错误了。如果在启动其它项目(特指Acme.BookStore.Web项目)的时候报同样的错误,那么同样执行命令abp install-libs即可解决问题。同时启动这3个项目如下:b1301539fd914390e350c9d7819a2156.png启动成功后就可以见到熟悉的界面:16bea4028d1f36010fb151f05c7983bd.png下面是项目Swagger的界面:07c6ae7fa3ac947aa3144dd49a3fd93e.png至此,已经把从官方下载下来的项目成功地运行起来了。

2.ABP运行流程

下面是在网上[1]找到的一张图,很清晰的说明了AspNet Core和ABP模块的运行流程,个人认为图上的Startup.ConfigureServices应该是Startup.Configure,已经在图中做了修改。bae0c78b5322f37061cb172244751669.png(1)AspNet Core运行流程
简单理解,基本上就是在Startup.ConfigureServices中进行依赖注入配置,然后在Startup.Configure中配置管道中间件,访问的时候就像洋葱模型。
(2)ABP模块运行流程

  • 在ABP模块中对Startup.ConfigureServices做了扩展,增加了PreConfigureServices和PostConfigureServices。对Startup.Configure也做了扩展,当然名字也修改了,Startup.Configure相当于是OnApplicationInitialization,同时增加了OnPreApplicationInitialization和OnPostApplicationInitialization。

  • 在ABP解决方案中有多个项目,每个项目都会有一个类继承自AbpModule。并且通过DependsOn描述了该模块依赖的模块。这样在ABP解决方案中就会有很多模块之间的依赖关系,通过拓扑排序算法对模块进行排序,从最深层的模块依次加载,直到启动所有模块。

(3)AbpModule抽象类中的方法 除了主要的PreConfigureServices()、ConfigureServices()、PostConfigureServices()、OnPreApplicationInitialization()、OnApplicationInitialization()、OnPostApplicationInitialization()方法外,还有一些其它的方法。abp\framework\src\Volo.Abp.Core\Volo\Abp\Modularity\AbpModule.csbbe263895181fb6c66ebf972fc7cb0ff.png

二.手动创建解决方案

0.创建解决方案

首先创建一个目录BookStoreHand用于存放解决方案:64f3ecb631c6ca754f6271f0b2d8e7cc.png然后创建一个解决方案,执行命令dotnet new sln -n Acme.BookStore:008839531c84b2431dc32c7aa1060a54.png用Rider打开后解决方案是空的,然后手动创建2个New Solution Folder,分别为src和test:1267e211cdea17ce185fb06e9e0dc229.png

1.创建领域共享层和领域层

(1)Acme.BookStore.Domain.Shared[领域共享层]
通常定义的常量和枚举,都放在该项目中。通过命令dotnet new classlib -n Acme.BookStore.Domain.Shareddotnet sln ../Acme.BookStore.sln add Acme.BookStore.Domain.Shared创建领域共享层,并将其添加到解决方案当中:07cb75c3781edd7c3d01444ed7a3e5d4.png然后就是创建模块类BookStoreDomainSharedModule如下:

namespace Acme.BookStore.Domain.Shared
{public class BookStoreDomainSharedModule: AbpModule{public override void ConfigureServices(ServiceConfigurationContext context){base.ConfigureServices(context);}public override void OnApplicationInitialization(ApplicationInitializationContext context){base.OnApplicationInitialization(context);}}
}

说明:接下来创建项目、添加项目间的引用等都使用Rider,而不再使用CLI操作,觉得CLI并不方便操作。基本思路顺序都是:创建项目,设置引用关系,创建模块,其它操作。
(2)Acme.BookStore.Domain[领域层]
该项目包含实体、值对象、领域服务、规约、仓储接口等。通过Rider创建Class Library项目Acme.BookStore.Domain如下:2de65424c708e9c68bc9f1b333f477b6.pngAcme.BookStore.Domain项目依赖于Acme.BookStore.Domain.Shared项目:954cd186a2aee83d382051a01aa5819b.png创建模块类BookStoreDomainSharedModule如下:

[DependsOn(typeof(BookStoreDomainSharedModule) //依赖领域共享模块
)]
public class BookStoreDomainModule: AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){base.ConfigureServices(context);}public override void OnApplicationInitialization(ApplicationInitializationContext context){base.OnApplicationInitialization(context);}
}

创建领域实体Book:

public class Book: Entity<int>
{public string BookName { get; set; } //名字public string Author { get; set; } //作者public DateTime PublishDate { get; set; } //出版日期public double Price { get; set; } //价格
}

创建仓储IBookRepository接口:

public interface IBookRepository: IRepository<Book, int>
{
}

2.创建基础设施层

(1)创建项目
基础设施层Acme.BookStore.EntityFrameworkCore是EF Core核心基础依赖项目,包含数据上下文、数据库映射、EF Core仓储实现等。通过Rider创建Class Library项目Acme.BookStore.EntityFrameworkCore如下:3b54a509f7291a211862c1ff919a99aa.pngAcme.BookStore.EntityFrameworkCore项目依赖于Acme.BookStore.Domain项目:f5fe3083d0caa0ca1cdbd8fdf5301bf5.png(2)创建模块
创建模块类BookStoreEntityFrameworkCoreModule如下:

[DependsOn(typeof(BookStoreDomainModule),typeof(AbpEntityFrameworkCoreSqlServerModule)
)]
public class BookStoreEntityFrameworkCoreModule: AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){context.Services.AddAbpDbContext<BookStoreDbContext>(options =>{// 给所有的实体都增加默认仓储options.AddDefaultRepositories(includeAllEntities: true);});Configure<AbpDbContextOptions>(options =>{options.UseSqlServer();});}public override void OnApplicationInitialization(ApplicationInitializationContext context){base.OnApplicationInitialization(context);}
}

(3)创建数据库上下文
创建数据库上下文BookStoreDbContext:

public class BookStoreDbContext: AbpDbContext<BookStoreDbContext>
{public BookStoreDbContext(DbContextOptions<BookStoreDbContext> options) : base(options){}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfigurationsFromAssembly(typeof(BookStoreDbContext).Assembly);}
}

ApplyConfigurationsFromAssembly应用来自IEntityTypeConfiguration中的配置。定义实体映射BookDbMapping如下:

public class BookDbMapping: IEntityTypeConfiguration<Book>
{public void Configure(EntityTypeBuilder<Book> builder){// 配置主键builder.HasKey(b => b.Id).HasName("Id");// 配置表和字段builder.ToTable("AbpBook");builder.Property(t => t.BookName).IsRequired().HasColumnName("BookName").HasComment("书名");builder.Property(t => t.Author).IsRequired().HasColumnName("Author").HasComment("作者");builder.Property(t => t.PublishDate).IsRequired().HasColumnName("PublishDate").HasComment("出版日期");builder.Property(t => t.Price).IsRequired().HasColumnName("Price").HasComment("价格");// 配置关系}
}

(4)创建仓储实现 定义IBookRepository的实现BookRepository如下:

public class BookRepository: EfCoreRepository<BookStoreDbContext, Book, int>, IBookRepository
{public BookRepository(IDbContextProvider<BookStoreDbContext> dbContextProvider) : base(dbContextProvider){}
}

3.创建应用契约层和应用层

(1)Acme.BookStore.Application.Contracts[应用契约层]
包含应用服务接口和数据传输对象。该项⽬被应⽤程序客户端引用,比如Web项目、API客户端项目。通过Rider创建Class Library项目Acme.BookStore.Application.Contracts:71065766413483f9d71eb14fda2d4b42.pngAcme.BookStore.Application.Contracts项目依赖于Acme.BookStore.Domain.Shared项目如下:f28797ccabeab715d4b11907621860e3.png创建模块类BookStoreApplicationContractsModule如下:

[DependsOn(typeof(BookStoreDomainSharedModule), //依赖于BookStoreDomainSharedModuletypeof(AbpObjectExtendingModule) //依赖于AbpObjectExtendingModule
)]
public class BookStoreApplicationContractsModule: AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){base.ConfigureServices(context);}public override void OnApplicationInitialization(ApplicationInitializationContext context){base.OnApplicationInitialization(context);}
}

创建服务接口IBookAppService如下:

public interface IBookAppService: IApplicationService
{/// <summary>/// 获取书籍/// </summary>Task<BookDto> GetBookAsync(int id);
}

创建输出DTO为BookDto如下:

public class BookDto
{public int Id { get; set; } //主键public string BookName { get; set; } //名字public string Author { get; set; } //作者public DateTime PublishDate { get; set; } //出版日期public double Price { get; set; } //价格
}

(2)Acme.BookStore.Application[应用层]
实现在Contracts项目中定义的接⼝。通过Rider创建Class Library项目Acme.BookStore.Application如下:b22a4a57a7bac22cd8b5d33b4fd9e9de.pngAcme.BookStore.Application项目依赖于 Acme.BookStore.Application.Contracts和Acme.BookStore.Domain项目:6eb38f96ddf4a9dbfb53d6a1537daf04.png创建模块类BookStoreApplicationModule如下:

[DependsOn(typeof(AbpAutoMapperModule), //依赖于AutoMappertypeof(BookStoreDomainModule), //依赖于BookStoreDomainModuletypeof(BookStoreApplicationContractsModule) //依赖于BookStoreApplicationContractsModule
)]
public class BookStoreApplicationModule: AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){var services = context.Services;// 添加ObjectMapper注入services.AddAutoMapperObjectMapper<BookStoreApplicationModule>();// Abp AutoMapper设置Configure<AbpAutoMapperOptions>(config =>{config.AddMaps<BookStoreApplicationAutoMapperProfile>();});}public override void OnApplicationInitialization(ApplicationInitializationContext context){base.OnApplicationInitialization(context);}
}

创建自动映类BookStoreApplicationAutoMapperProfile如下:

public class BookStoreApplicationAutoMapperProfile: Profile
{public BookStoreApplicationAutoMapperProfile(){CreateMap<BookDto, Book>();CreateMap<Book, BookDto>();}
}

创建IBookAppService类的实现类BookAppService如下:

public class BookAppService: ApplicationService, IBookAppService
{private readonly IBookRepository _bookRepository;public BookAppService(IBookRepository bookRepository){_bookRepository = bookRepository;}public async Task<BookDto> GetBookAsync(int id){var queryable = await _bookRepository.GetQueryableAsync();var book = queryable.FirstOrDefault(t => t.Id == id);if (book == null){throw new ArgumentNullException(nameof(book));}return ObjectMapper.Map<Book, BookDto>(book);}
}

4.创建种子迁移

Acme.BookStore.DbMigrator是控制台应用程序,主要是迁移数据库结构并初始化种子数据。通过Rider创建ASP.NET Core Web Application的Empty项目Acme.BookStore.DbMigrator如下:834b5a2608866a48590fa487c4853714.pngAcme.BookStore.DbMigrator项目依赖于Acme.BookStore.Application.Contracts和Acme.BookStore.EntityFrameworkCore项目如下:6edd65e9e04fc94640d67f35e3f21a05.png创建模块类BookStoreDbMigratorModule如下:

[DependsOn(typeof(AbpAutofacModule),typeof(BookStoreEntityFrameworkCoreModule),typeof(BookStoreApplicationContractsModule))]
public class BookStoreDbMigratorModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){base.ConfigureServices(context);}public override void OnApplicationInitialization(ApplicationInitializationContext context){base.OnApplicationInitialization(context);}
}

在Program.cs中添加services.AddHostedService()这个数据库迁移主机服务,在DbMigratorHostedService类中包含StartAsync()和StopAsync()这2个方法,在StartAsync()中获取BookStore数据库迁移服务BookStoreDbMigrationService,并执行数据库迁移方法MigrateAsync()。数据库迁移的思路基本上就是在Acme.BookStore.DbMigrator目录下执行命令:dotnet ef migrations add InitialCreate和dotnet ef database update,只不过使用的C#代码来实现的。自己通过Acme.BookStore.DbMigrator项目没有迁移成功,最后还是通过命令行实现迁移的。迁移结果如下:
6aa2e7103dd90805b78ecce15d3ac14d.png说明:Program.cs、DbMigratorHostedService.cs和BookStoreDbMigrationService.cs的源码等完整项目源码参考[4]。

5.创建远程服务层

Acme.BookStore.HttpApi[远程服务层],简单理解就是很薄的控制层,该项目主要用于定义HTTP API,即应用服务层的包装器,将它们公开给远程客户端调用。通过Rider创建Class Library项目Acme.BookStore.HttpApi如下:
6def3dcd6ee89230465e0de5a4214e61.pngAcme.BookStore.HttpApi项目依赖于Acme.BookStore.Application.Contracts项目如下:5b43eca762af5f3a9e34d32c99bbe757.png创建模块类BookStoreHttpApiModule如下:

[DependsOn(typeof(BookStoreApplicationContractsModule) //依赖于BookStoreApplicationContractsModule
)]
public class BookStoreHttpApiModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){base.ConfigureServices(context);}public override void OnApplicationInitialization(ApplicationInitializationContext context){base.OnApplicationInitialization(context);}
}

为了简要说明问题,创建一个简单的控制器类BookStoreController如下:

[RemoteService]
[Area("BookStore")]
[Route("api/app/book")]
public class BookStoreController: AbpControllerBase
{private readonly IBookAppService _bookAppService;public BookStoreController(IBookAppService bookAppService){_bookAppService = bookAppService;}[HttpGet][Route("get-book")]public Task<BookDto> GetBookAsync(int id){return _bookAppService.GetBookAsync(id);}
}

6.创建展示层

Acme.BookStore.HttpApi.Host这个是前后端分离时的项目命名方式。通过Rider创建ASP.NET Core Web Application的Empty项目Acme.BookStore.HttpApi.Host如下:49904e4fc9fd1d1ce9546c0207356394.pngAcme.BookStore.HttpApi.Host项目依赖于Acme.BookStore.Application、Acme.BookStore.EntityFrameworkCore和Acme.BookStore.HttpApi项目如下:e2cc1acd931d70d89d298e61dfb3711c.png创建模块类BookStoreHttpApiHostModule如下:

[DependsOn(typeof(BookStoreHttpApiModule), //依赖于BookStoreHttpApiModuletypeof(AbpAutofacModule), //依赖于AbpAutofacModuletypeof(BookStoreApplicationModule), //依赖于BookStoreApplicationModuletypeof(BookStoreEntityFrameworkCoreModule), //依赖于BookStoreEntityFrameworkCoreModuletypeof(AbpAspNetCoreSerilogModule), //依赖于AbpAspNetCoreSerilogModuletypeof(AbpSwashbuckleModule) //依赖于AbpSwashbuckleModule
)]
public class BookStoreHttpApiHostModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){var services = context.Services;var configuration = services.GetConfiguration();ConfigureConventionalControllers();ConfigureCors(context, configuration);ConfigureSwaggerServices(context, configuration);}private void ConfigureConventionalControllers(){Configure<AbpAspNetCoreMvcOptions>(options =>{options.ConventionalControllers.Create(typeof(BookStoreApplicationModule).Assembly);});}private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration){context.Services.AddCors(options =>{options.AddPolicy("AllowAll",builder =>{builder.WithOrigins(configuration["App:CorsOrigins"].Split(",", StringSplitOptions.RemoveEmptyEntries).Select(o => o.RemovePostFix("/")).ToArray()).WithAbpExposedHeaders().SetIsOriginAllowedToAllowWildcardSubdomains().AllowAnyHeader().AllowAnyMethod().AllowCredentials();});});}private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration){context.Services.AddSwaggerGen(options =>{options.SwaggerDoc("v1", new OpenApiInfo { Title = "BookStore API", Version = "v1" });options.DocInclusionPredicate((docName, description) => true);options.CustomSchemaIds(type => type.FullName);});}public override void OnApplicationInitialization(ApplicationInitializationContext context){var env = context.GetEnvironment();var app = context.GetApplicationBuilder();var configuration = context.GetConfiguration();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseCors("AllowAll");if (configuration["UseSwagger"] == "true"){app.UseSwagger();app.UseSwaggerUI(options =>{options.SwaggerEndpoint("/swagger/v1/swagger.json", "Acme.BookStore API");});}app.UseRouting();app.UseConfiguredEndpoints();}
}

说明:HomeController.cs、Program.cs和配置文件等项目完整源码参考[4]。
将Acme.BookStore.HttpApi.Host项目启动起来后如下:0734e1b037ac943fbac4445ec3d205ba.png通过Swagger界面测试https://localhost:7016/api/app/book/get-book?id=1接口如下:61c8203185136a367e2df1d040f219a9.png

  奇怪的是在线生成解决方案的时候,UI框架选择了MVC,但是还是出现了这个项目,并且在启动Acme.BookStore.Web项目的时候,如果不启动Acme.BookStore.HttpApi.Host项目,还会报错Volo.Abp.AbpException: Remote service 'AbpMvcClient' was not found and there is no default configuration,并且还没有找到Acme.BookStore.Web项目在哪里用到了Acme.BookStore.HttpApi.Host项目。因为自己主要关注前后端分离的项目,所以就不纠结这个细节了。
  在线生成解决方案中还包括:Acme.BookStore.IdentityServer(认证授权项目),Acme.BookStore.HttpApi.Client(远程服务代理层),Acme.BookStore.Web(前后端不分离的展示层),Acme.BookStore.TestBase(其它项目共享或使用的类),Acme.BookStore.Domain.Tests(测试领域层对象),Acme.BookStore.EntityFrameworkCore.Tests(测试自定义仓储实现或EF Core映射),Acme.BookStore.Application.Tests(测试应用层对象),Acme.BookStore.HttpApi.Client.ConsoleTestApp(从.NET控制台中调用HTTP API)等。一篇文章放不下,后面继续解说实践。

参考文献:
[1]聊一聊ABP vNext的模块化系统:https://www.sohu.com/a/436373048_468635
[2]Abp vNext源码分析文章目录:https://www.cnblogs.com/myzony/p/10722506.html
[3]手动从0搭建ABP框架-ABP官方完整解决方案源码:https://url39.ctfile.com/f/2501739-625678611-787336?p=2096 (访问密码: 2096)
[4]手动从0搭建ABP框架-手动搭建简化解决方案源码:https://url39.ctfile.com/f/2501739-625678627-091eb9?p=2096 (访问密码: 2096)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284321.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java捕获并处理线程失败抛出的异常

使用 UncaughtExceptionHandler 示例代码如下&#xff1a; Thread.UncaughtExceptionHandler handler new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) {System.out.println("Uncaught exception: " ex);} }; Th…

【ArcGIS微课1000例】0044:ArcGIS使用山体阴影显示DEM的3种方法

本文讲解了ArcGIS使用山体阴影显示DEM的3种方法:“影像分析”窗口、使用山体阴影效果和山体阴影效果工具的不同之处。 文章目录 一、“影像分析”窗口二、使用山体阴影效果三、山体阴影工具一、“影像分析”窗口 使用山体阴影显示 DEM 的方法有两种。最简单并且最具交互效果的…

区块链每日投资指南(0129)-证监会副主席表示数字货币需要监管

上一周的走势依然是工作日下跌&#xff0c;周末拉升的结局。这主要原因依然是&#xff0c;周末不上班。最终政策出炉之前&#xff0c;市场恐怕还将继续震荡。下周的工作日恐怕会重演下跌的节奏。但是经过了17号&#xff0c;23号&#xff0c;26号三次筑底来看&#xff0c;如果政…

蓝绿发布、滚动发布、灰度发布,有什么区别?

在项目迭代的过程中&#xff0c;不可避免需要”上线“。上线对应着部署&#xff0c;或者重新部署&#xff1b;部署对应着修改&#xff1b;修改则意味着风险。目前有很多部署发布的技术, 这儿将常见的做一个总结。 上面所说难免有些抽象, 举一个情景例子, 加入你是微博项目负责…

iOS 音频开发

音频基础知识 组成 音频文件的组成&#xff1a;文件格式(或者音频容器) 数据格式(或者音频编码)。 文件格式(或音频容器)是用于形容文件本身的格式。 我们可以通过多种不同的方法为真正的音频数据编码。例如CAF文件便是一种文件格式&#xff0c;它能够包含MP3格式&#xff0c;…

【ArcGIS微课1000例】0045:ArcGIS制图模板的自定义与使用方法

怎样在ArcGIS中保存地图模板以在地图制图与打印之前使用呢? 文章目录 一、地图模板简介二、地图模板创建1. 创建模板2. 创建缩略图3. 保存模板三、地图模板使用一、地图模板简介 使用ArcMap打开一个已有的地图模板,【文件】→【新建】,任选一个模板,这里选择一个传统模板。…

api 接口开发理论 在php中调用接口以及编写接口

如&#xff1a;http://localhost/openUser.php?actget_user_list&typejson 在这里openUser.php相当于一个接口&#xff0c;其中get_user_list 是一个API&#xff08;获取用户列表&#xff09;&#xff0c;讲求返回的数据类型为JSON格式。 你只需要在你PHP代码中执行这条链…

怎么样的框架对于开发者是友好的?

云原生离.NET开发到底有多远&#xff1f;云原生的概念由来不久&#xff0c;故事从“上云”开始&#xff0c;伴随dorker、k8s等技术的推出&#xff0c;以及CNCF与各大云厂商的共同加持&#xff0c;云原生逐渐被大家所熟知。云原生不依赖具体的云&#xff0c;不管公有云还是私有云…

JS 烧脑面试题大赏

本文精选了20多道具有一定迷惑性的js题&#xff0c;主要考察的是类型判断、作用域、this指向、原型、事件循环等知识点&#xff0c;每道题都配有详细傻瓜式的解析&#xff0c;偏向于初学者&#xff0c;大佬请随意。 第1题 let a 1 function b(a) {a 2console.log(a) } b(a)…

苹果ios用js的Date()出现NaN问题解决办法

原文:苹果ios用js的Date&#xff08;&#xff09;出现NaN问题解决办法ios使用如下方法获得NaN&#xff0c;安卓手机则是正常计算&#xff0c;解决方法是换个这个时间的格式 new Date("2017-04-28 23:59:59").getTime() 换成如下方式就正常了&#xff0c;就是‘-’换成…

Thinkphp 验证码、文件上传

一、验证码 验证码参数 例题&#xff1a;登录时验证下验证码 LoginController.class.php <?php namespace Home\Controller; use Think\Controller; class LoginController extends Controller {public function Login(){if(empty($_POST)){$this->display(); } e…

ArcGIS实验教程——实验四十七:数据驱动页工具批量制作甘肃省各地级市人口七普专题图集

本实验详细讲解利用ArcGIS数据驱动页工具,制作甘肃省各地级市人口七普专题图集。 文章目录 1. 数据驱动页工具简介2. 甘肃省各地级市人口七普专题图集2.1 符号化及标注2.2 数据驱动页的创建2.3 数据驱动页面文本操作2.4 数据驱动页的导出1. 数据驱动页工具简介 数据驱动页面是…

为什么Java有GC调优而没听说过有CLR的GC调优?

前言在很多的场合我都遇到过一些群友提这样的一些问题&#xff1a;为什么Java有GC调优而CLR没有听说过有GC调优呢&#xff1f;到底是Java的JVM GC比较强还是C#使用的.NET CLR的GC比较强呢&#xff1f;其实业内已经有几位大佬的高赞文章和大家分享一下&#xff0c;主要讨论JVM和…

Ubuntu16.04 - 安装RabbitVCS,linux下的TortoiseSVN!!!

RabbitVCS 官网&#xff1a;http://rabbitvcs.org/ 1&#xff0c;添加PPA源。在shell里面执行下面命令&#xff1a; sudo add-apt-repository ppa:rabbitvcs/ppa 这个命令执行完毕后&#xff0c;查看执行结果看是否密钥导入成功&#xff0c;成功截图&#xff1a; 如果导入密钥失…

8 种最坑的SQL错误用法

1、LIMIT 语句 2、隐式转换 3、关联更新、删除 4、混合排序 5、EXISTS语句 6、条件下推 7、提前缩小范围 8、中间结果集下推 总结 sql语句的执行顺序&#xff1a; FROM <left_table>ON <join_condition><join_type>JOIN <right_table>WHERE &…

Vue根据菜单json数据动态按需加载路由Vue-router

每个菜单项对应一个页面组件&#xff0c;根据菜单项动态按需加载路由 路由配置的正确写法&#xff1a; /*router/index.js*/ import Vue from vue import Router from vue-router import url from ./url import store from ../storeVue.use(Router)const router new Router({/…

【ArcGIS微课1000例】0047:制图表达(2)---河流渐变效果的实现

当我们在ArcMap中加载河流数据时,得到的效果往往如图所示,仅仅是表示河流位置的线要素,既无法真实地反映河流的实际情况,同时在出图的时候也远没有任何美化效果。 文章目录 1.创建制图表达2.添加几何效果3.使用制图规则4.使用制图表达属性覆盖警告:这些操作会对您的数据库…

操作系统思考 第二章 进程

第二章 进程 作者&#xff1a;Allen B. Downey 原文&#xff1a;Chapter 2 Processes 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 2.1 抽象和虚拟化 在我们谈论进程之前&#xff0c;我打算先定义几个东西&#xff1a; 抽象&#xff08;Abstraction&#xff09;&…

1 句代码,搞定 ASP.NET Core 绑定多个源到同一个类

问题有群友希望将路由中的信息绑定到一个Dto对象中&#xff1a;public class DDDDDto {[FromRoute(Name "collectionId")]public Guid collectionId { get; set; }[BindProperty(Name "relativeUrl")]public string relativeUrl { get; set; } }这样就不用…

redux中间件的用法

1.定义 中间件就是一个函数&#xff0c;对store.dispatch方法进行了改造&#xff0c;在发出 Action 和执行 Reducer 这两步之间&#xff0c;添加了其他功能。 2.举例 日志中间件 import { applyMiddleware, createStore } from redux; import createLogger from redux-logger; …