手动从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;修改则意味着风险。目前有很多部署发布的技术, 这儿将常见的做一个总结。 上面所说难免有些抽象, 举一个情景例子, 加入你是微博项目负责…

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

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

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

云原生离.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)…

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; 如果导入密钥失…

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

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

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; } }这样就不用…

设置git自动补全功能(windows版本)

目录 下载 Git 的源代码 在目录中 git/contrib/completion/ 中找到 git-completion.bash 文件 将 git-completion.bash 文件改名为 .git-completion.bash 找到本机git安装目录 将.git-completion.bash文件复制到git安装目录下的etc文件夹 打开同目录下的 bash.bashrc 文件&…

[转]Java 18 还未用上,Java 19 最新两大特性曝光

铁打的 Java&#xff0c;流水的版本。 不久前&#xff0c;Java 18 才正式发布&#xff0c;遵循 Oracle 六个月发一版本的频率&#xff0c;Java 19 将在今年 9 月出炉。这不&#xff0c;还没等众多开发者用上 Java 18&#xff0c;关于 Java 19 最新的两个目标功能就被披露了出…

C# 值类型和引用类型讲解

要了解值类型和引用类型&#xff0c;我们首先要知道堆和栈的区别&#xff1a;① 栈是编译期间就分配好的内存空间&#xff0c;因此你的代码中必须就栈的大小有明确的定义&#xff1b;堆是程序运行期间动态分配的内存空间&#xff0c;你可以根据程序的运行情况确定要分配的堆内存…

【ArcGIS微课1000例】0048:制图表达(3)---水立方效果实现

本文讲解ArcGIS中水立方效果的实现过程(制图表达案例)。 文章目录 一、效果展示二、制作步骤1. 创建数据库及要素数据集2. 创建范围3. 创建随机点4. 创建泰森多边形5. 创建制图表达一、效果展示 基于制图表达的思想,可以容易实现多种形式的水立方效果,例如: 怎么实现的呢…

Java中this与super的区别

2019独角兽企业重金招聘Python工程师标准>>> this与super关键字在java中构造函数中的应用&#xff1a; ** super()函数 ** super()函数在子类构造函数中调用父类的构造函数时使用&#xff0c;而且必须要在构造函数的第一行&#xff0c;例如&#xff1a; class Ani…

EF选择Mysql数据源

EF添加ADO.NET实体模型处直接选择Mysql数据源 最近想到EF是连接多数据库的orm框架&#xff0c;于是就想测试下。查了一堆网上资料后&#xff0c;测试连接mysql成功。步骤如下&#xff1a; 1、在你项目Model层中nuget安装MySql.Data.Entity 如果没安装这个provider 就进行下面的…

JIRA简介及基本概念

目录 第一章 JIRA简介 1.1 什么是JIRA 1.2 JIRA的主要功能 1.3 JIRA的主要特点 1.3.1 JIRA的优点 1.3.2 JIRA的缺点 1.4 相关版本 第二章 JIRA的基本概念 2.1 JIRA 中涉及的角色 2.1.1 管理人员 2.1.2 项目管理者 2.1.3 开发人员 2.1.4 测试人员 2.2 问题 2.2.1…