从头编写 asp.net core 2.0 web api 基础框架 (4) EF配置

Github源码地址:https://github.com/solenovex/Building-asp.net-core-2-web-api-starter-template-from-scratch

前三部分弄完,我们已经可以对内存数据进行CRUD的基本操作,并且可以在asp.net core 2中集成Nlog了。

下面继续:

Entity Framework Core 2.0

Entity Framework 是ORM(Object-Relational-Mapping)。ORM是一种让你可以使用面向对象的范式对数据库进行查询和操作。

简单的情况下,ORM可以把数据库中的表和Model对象一一映射起来;也有比较复杂的情况,ORM允许使用OO(面向对象)功能来做映射,例如:Person作为基类,Employee作为Person的派生类,他们俩可以在数据库中映射成一个表;或者在没有继承的情况下,数据库中的一个表可能和多个类有映射关系。

EF Core 不是 EF6的升级版,这个大家应该知道,EF Core是轻量级、具有很好的扩展性的,并且是跨平台的EF版本。

EF Core 目前有很多Providers,所以支持很多种数据库,包括:MSSQL,SQLite,SQL Compact,Postgres,MySql,DB2等等。而且还有一个内存的Provider,用于测试和开发。开发UWP应用的时候也可以使用EF Core(用SQLite Provider)。

EF Core支持两种模式:

Code First:简单理解为 先写C#(Model),然后生成数据库。

Database First:现在数据库中建立表,然后生成C#的Model。

由于用asp.net core 2.0开发的项目基本都是新项目,所以建议使用Code First。

创建 Entity

Entity就是普通的C#类,就像Dto一样。Dto是与外界打交道的Model,entity则不一样,有一些Dto的计算属性我们并不像保存在数据库中,所以entity中没有这些属性;而数据从entity传递到Dto后某些属性也会和数据库里面的形式不一样。

首先把我们原来的Product和Material这两个Dto的名字重构一下,改成ProductDto和MaterialDto。

建立一个Entities文件夹,在里面建立Product.cs:

namespace CoreBackend.Api.Entities
{  
 public class Product{      
 public int Id { get; set; }    
   public string Name { get; set; }  
      public float Price { get; set; }} }

DbContext

EFCore使用一个DbContext和数据库打交道,它代表着和数据库之间的一个Session,可以用来查询和保存我们的entities。

DbContext需要一个Provider,以便能访问数据库(这里我们就用LocalDB吧)。

我们就建立一个DbContext吧(大一点的项目会使用多个DbContext)。建立MyContext并集成DbContext:

namespace CoreBackend.Api.Entities
{   
 public class MyContext : DbContext{      
 public DbSet<Product> Products { get; set; }} }


这里我们为Product建立了一个类型为DbSet<T>的属性,它可以用来查询和保存实例(针对DbSet的Linq查询语句将会被解释成针对数据库的查询语句)。

因为我们需要使用这个MyContext,所以就需要先在Container中注册它,然后就可以在依赖注入中使用了。

打开Startup.cs,修改ConfigureServices,添加这一句话:

services.AddDbContext<MyContext>();

使用AddDbContext这个Extension method为MyContext在Container中进行注册,它默认的生命周期使Scoped。

但是它如何连接数据库?这就需要连接字符串,我们需要为DbContext提供连接字符串,这里有两种方式。

第一种是在MyContext中override OnConfiguring这个方法:

namespace CoreBackend.Api.Entities
{    public class MyContext : DbContext{       
 public DbSet<Product> Products { get; set; }      
 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseSqlServer("xxxx connection string");base.OnConfiguring(optionsBuilder);}} }

其中的参数optionsBuilder提供了一个UseSqlServer()这个方法,它告诉Dbcontext将会被用来连接Sql Server数据库,在这里就可以提供连接字符串,这就是第一种方法。

第二种方法:

先大概看一下DbContext的源码的定义:

namespace Microsoft.EntityFrameworkCore
{    public class DbContext : IDisposable, IInfrastructure<IServiceProvider>, IDbContextDependencies, IDbSetCache, IDbContextPoolable{       
 public DbContext([NotNullAttribute] DbContextOptions options);

有一个Constructor带有一个DbContextOptions参数,那我们就在MyContext种建立一个Constructor,并overload这个带有参数的Constructor。


namespace CoreBackend.Api.Entities
{   
  public class MyContext : DbContext{      
   public MyContext(DbContextOptions<MyContext> options):base(options){}      
     public DbSet<Product> Products { get; set; }} }


这种方法相对第一种的优点是:它可以在我们注册MyContext的时候就提供options,显然这样做比第一种override OnConfiguring更合理。

然后返回Startup:


        public void ConfigureServices(IServiceCollection services){services.AddMvc();
#if DEBUGservices.AddTransient<IMailService, LocalMailService>();
#elseservices.AddTransient<IMailService, CloudMailService>();
#endifvar connectionString = @"Server=(localdb)\MSSQLLocalDB;Database=ProductDB;Trusted_Connection=True";services.AddDbContext<MyContext>(o => o.UseSqlServer(connectionString));}


使用AddDbContext的另一个overload的方法,它可以带一个参数,在里面调用UseSqlServer。

关于连接字符串,我是用的是LocalDb,实例名是MSSQLLocalDB。可以在命令行查询本机LocalDb的实例,使用sqllocaldb info:

也可以通过VS的Sql Server Object Explorer查看:

连接字符串中的ProductDb是数据库名;连接字符串的最后一部分表示这是一个受信任的连接,也就是说使用了集成验证,在windows系统就是指windows凭证。

生成数据库

因为我们使用的是Code First,所以如果还没有数据库的话,它应该会自动建立一个数据库。

打开MyContext:

        public MyContext(DbContextOptions<MyContext> options):base(options){            Database.EnsureCreated();}

这个Constructor在被依赖注入的时候会被调用,在里面写Database.EnsureCreated()。其中Database是DbContext的一个属性对象。

EnsureCreated()的作用是,如果有数据库存在,那么什么也不会发生。但是如果没有,那么就会创建一个数据库。

但是现在就运行的话,并不会创建数据库,因为没有创建MyContext的实例,也就不会调用Constructor里面的内容。

那我们就建立一个临时的Controller,然后注入MyContext,此时就调用了MyContext的Constructor:


namespace CoreBackend.Api.Controllers
{[Route("api/[controller]")]   
 public class TestController: Controller{      
   private MyContext _context;public TestController(MyContext context){_context = context;}[HttpGet]    
   public IActionResult Get(){        
      return Ok();}} }

使用Postman访问Get这个Action后,我们可以从Debug窗口看见一些创建数据库和表的Sql语句:

然后我们查看一下Sql Server Object Explorer:

我们可以看到数据库建立好了,里面还有dbo.Products这个表。

Database.EnsureCreated()确实可以保证创建数据库,但是随着代码不断被编写,我们的Model不断再改变,数据库应该也随之改变,而EnsureCreated()就不够了,这就需要迁移(Migration)。

不过迁移之前,我们先看看Product这个表的具体字段属性:

Product的Id作为了主键,而Name这个字符串的长度是max,而Price没有精度限制,这样不行。我们需要对Model生成的表的字段进行限制!

解释一下:Product这个entity中的Id,根据约定(Id或者ProductId)会被视为映射表的主键,并且该主键是自增的。

如果不使用Id或者ProductId这两个名字作为主键的话,我们可以通过两种方式把该属性设置成为主键:Data Annotation注解和Fluet Api。我只在早期使用Data Annotation,后来一直使用Fluent Api,所以我这里只介绍Fluent Api吧。

Fluet Api

针对Product这个entity,我们要把它映射成一个数据库的表,所以针对每个属性,可能需要设定一些限制,例如最大长度,是否必填等等。

针对Product,我们可以在MyContext里面override OnModelCreating这个方法,然后这样写:

        protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.Entity<Product>().HasKey(x => x.Id);modelBuilder.Entity<Product>().Property(x => x.Name).IsRequired().HasMaxLength(50);modelBuilder.Entity<Product>().Property(x => x.Price).HasColumnType("decimal(8,2)");}

第一行表示设置Id为主键(其实我们并不需要这么做)。然后Name属性是必填的,而且最大长度是50。最后Price的精度是8,2,数据库里的类型为decimal。

fluent api有很多方法,具体请查看文档:https://docs.microsoft.com/en-us/ef/core/modeling/

然后,我们就会发现一个严重的问题。如果项目里面有很多entity,那么所有的fluent api配置都需要写在OnModelCreating这个方法里,那太多了。

所以我们改进一下,使用IEntityTypeConfiguration<T>。建立一个叫ProductConfiguration的类:

    public class ProductConfiguration : IEntityTypeConfiguration<Product>{    
   public void Configure(EntityTypeBuilder<Product> builder){            builder.HasKey(x => x.Id);builder.Property(x => x.Name).IsRequired().HasMaxLength(50);builder.Property(x => x.Price).HasColumnType("decimal(8,2)");}}

把刚才在MyContext里写的配置都移动到这里,然后修改一些MyContext的OnModelCreating方法:

        protected override void OnModelCreating(ModelBuilder modelBuilder){            modelBuilder.ApplyConfiguration(new ProductConfiguration());}

就是把ProductConfiguration里面写的配置加载进来,和之前的效果是一样的。

但是项目中如果有很多entities的话也需要写很多行代码,更好的做法是写一个方法,可以加载所有实现了IEntityTypeConfiguration<T>的实现类。在老版的asp.net web api 2.2里面有一个方法可以从某个Assembly加载所有继承于EntityTypeConfiguration的类,但是entity framework core并没有提供类似的方法,以后我们自己写一个吧,现在先这样。

然后把数据库删掉,重新生成一下数据库:

很好!

迁移 Migration

随着代码的更改,数据库也会跟着变,所有EnsureCreated()不满足要求。migration就允许我们把数据库从一个版本升级到另一个版本。那我们就研究一下,首先把数据库删了,然后创建第一个迁移版本。

打开Package Manager Console,做个迁移 Add-Migration xxx:

Add-Migration 然后接着是一个你起的名字。

然后看一下VS的Solution Explorer 会发现生成了一个Migrations目录:

里面有两个文件,一个是Snapshot,它是目前entity的状态:


namespace CoreBackend.Api.Migrations
{[DbContext(typeof(MyContext))]  
 partial class MyContextModelSnapshot : ModelSnapshot{      
 
  protected override void BuildModel(ModelBuilder modelBuilder){#pragma warning disable 612, 618modelBuilder.HasAnnotation("ProductVersion", "2.0.0-rtm-26452").HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);modelBuilder.Entity("CoreBackend.Api.Entities.Product", b =>{b.Property<int>("Id").ValueGeneratedOnAdd();b.Property<string>("Name").IsRequired().HasMaxLength(50);b.Property<float>("Price").HasColumnType("decimal(8,2)");b.HasKey("Id");b.ToTable("Products");});#pragma warning restore 612, 618}} }

这就是当前Product这个Model的状态细节,包括我们通过Fluent Api为其添加的映射限制等。

另一个文件是xxxx_ProductInfoDbInitialMigration,下划线后边的部分就是刚才Add-Migration命令后边跟着的名字参数。


namespace CoreBackend.Api.Migrations
{    
public partial class ProductInfoDbInitialMigration : Migration{      
 protected override void Up(MigrationBuilder migrationBuilder){migrationBuilder.CreateTable(name: "Products",columns: table => new{Id = table.Column<int>(type: "int", nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),Price = table.Column<float>(type: "decimal(8,2)", nullable: false)},constraints: table =>{table.PrimaryKey("PK_Products", x => x.Id);});}        protected override void Down(MigrationBuilder migrationBuilder){migrationBuilder.DropTable(name: "Products");}} }

这里面包含着migration builder需要的代码,用来迁移这个版本的数据库。里面有Up方法,就是从当前版本升级到下一个版本;还有Down方法,就是从下一个版本再退回到当前版本。

我们也可以不使用 Add-Migration命令,手写上面这些代码也行,我感觉还是算了吧。

另外还有一件事,那就是要保证迁移migration都有效的应用于数据库了,那就是另一个命令 Update-Database。

先等一下,我们也可以使用代码来达到同样的目的,打开MyContext:

        public MyContext(DbContextOptions<MyContext> options): base(options){            Database.Migrate();}

把之前的EnsureCreated改成Database.Migrate(); 如果数据库还没删除,那就最后删除一次。

运行,并除法TestController:

然后会看见Product表,除此之外还有一个__EFMigrationHistory表,看看有啥:

这个表里面保存了哪些迁移已经被应用于这个数据库了。这也保证了Database.Migrate()或者Update-database命令不会执行重复的迁移migration。

我们再弄个迁移,为Product添加一个属性:

namespace CoreBackend.Api.Entities
{  
 public class Product{      
   public int Id { get; set; }      
     public string Name { get; set; }      
       public float Price { get; set; }    
     public string Description { get; set; }}  
   public class ProductConfiguration : IEntityTypeConfiguration<Product>{      
     public void Configure(EntityTypeBuilder<Product> builder){builder.HasKey(x => x.Id);builder.Property(x => x.Name).IsRequired().HasMaxLength(50);builder.Property(x => x.Price).HasColumnType("decimal(8,2)");            builder.Property(x => x.Description).HasMaxLength(200);}} }

执行Add-Migration后,会在Migrations目录生成了一个新的文件:


namespace CoreBackend.Api.Migrations
{    public partial class AddDescriptionToProduct : Migration{        protected override void Up(MigrationBuilder migrationBuilder){migrationBuilder.AddColumn<string>(name: "Description",table: "Products",type: "nvarchar(200)",maxLength: 200,nullable: true);}        protected override void Down(MigrationBuilder migrationBuilder){migrationBuilder.DropColumn(name: "Description",table: "Products");}}
}


然后这次执行Update-Database命令:

加上verbose参数就是显示执行过程的明细而已。

不用运行,看看数据库:

Description被添加上了,然后看看迁移表:

目前差不太多了,但还有一个安全隐患。它是:

如何安全的保存敏感的配置数据,例如:连接字符串

保存连接字符串,你可能会想到appSettings.json,但这不是一个好的想法。在本地开发的时候还没有什么问题(使用的是集成验证),但是你要部署到服务器的时候,数据库连接字符串可能包括用户名和密码(Sql Server的另一种验证方式)。加入你不小心把appSettings.json或写到C#里面的连接字符串代码提交到了Git或TFS,那么这个用户名和密码包括服务器的名称可能就被暴露了,这样做很不安全。

我们可以这样做,首先针对开发环境(development environment)把C#代码中的连接字符串拿掉,把它放到appSettings.json里面。然后针对正式生产环境(production environment),我们使用环境变量来保存这些敏感数据。

开发环境:

appSettings.json:


{  "mailSettings": {   
 "mailToAddress": "admin__json@qq.com",
    "mailFromAddress": "noreply__json@qq.com"},  "connectionStrings": {"productionInfoDbConnectionString": "Server=(localdb)\\MSSQLLocalDB;Database=ProductDB;Trusted_Connection=True"} }


Startup.cs:


        public void ConfigureServices(IServiceCollection services){services.AddMvc();
#if DEBUGservices.AddTransient<IMailService, LocalMailService>();
#elseservices.AddTransient<IMailService, CloudMailService>();
#endif            var connectionString = Configuration["connectionStrings:productionInfoDbConnectionString"];services.AddDbContext<MyContext>(o => o.UseSqlServer(connectionString));}


然后你可以设断点看看connectionString的值。目前项目的环境变量是Production,先改成Development:

然后断点调试:

可以看到这两个JsonConfigurationProvider就是appSettings的两个文件的配置。

这个就是appSettings.json,里面包含着我们刚才添加的连接字符串。

由于当前是Development环境,所以如果你查看另外一个JsonConfigurationProvider的话,会发现它里面的值是空的(Data数是0).

所以没有问题。

生产环境:

在项目的属性--Debug里面,我们看到了环境变量:

而这个环境变量,我们可以在程序中读取出来,所以可以在这里添加连接字符串:

注意它的key,要和appSettings.json里面的整体结构一致;Value呢应该是给一个服务器的数据库的字符串,这里就随便弄个假的吧。别忘了把Development改成Production。

然后调试一下:

没错。如果你仔细调试一下看看的话:就会从EnvironmentVariablesConfigurationProvider的第64个找到我们刚才写到连接字符串:

但是还没完。

打开项目的launchSettings.json:

你会发现:


{  "iisSettings": {   
 "windowsAuthentication": false,  
  "anonymousAuthentication": true,  
 "iisExpress": {    
  "applicationUrl": "http://localhost:60835/",  
    "sslPort": 0}},  "profiles": {  
 "IIS Express": {    
 "commandName": "IISExpress",    
   "launchBrowser": true,    
     "environmentVariables": {    
       "connectionStrings:productionInfoDbConnectionString": "Server=.;Database=ProductDB;UserId=sa;Password=pass;",        "ASPNETCORE_ENVIRONMENT": "Production"}},   "CoreBackend.Api": {  
   "commandName": "Project",    
    "launchBrowser": true,  
      "environmentVariables": {    
         "ASPNETCORE_ENVIRONMENT": "Development"},      "applicationUrl": "http://localhost:60836/"}} }


连接字符串在这里。这个文件一般都会源码控制给忽略,也不会在发布的时候发布到服务器。那么服务器怎么读取到这个连接字符串呢???

看上面调试EnvironmentVariablesConfigurationProvider的值,会发现里面有几十个变量,这些基本都不是来自launchSettings.json,它们是从系统层面上定义的!!

这回我们这样操作:

把launchSettings里面的连接字符串去掉:


{  "iisSettings": {    
"windowsAuthentication": false,  
 "anonymousAuthentication": true,  
   "iisExpress": {    
     "applicationUrl": "http://localhost:60835/",  
        "sslPort": 0}},  "profiles": {  
         "IIS Express": {    
          "commandName": "IISExpress",  
             "launchBrowser": true,    
               "environmentVariables": {"ASPNETCORE_ENVIRONMENT": "Production"}},    "CoreBackend.Api": {    
                "commandName": "Project",  
                   "launchBrowser": true,  
                       "environmentVariables": {
                 "ASPNETCORE_ENVIRONMENT": "Development"},      "applicationUrl": "http://localhost:60836/"}} }


然后这里自然也就没有了:

现在任何json文件都没有敏感信息了。

现在我们要把连接字符串添加到系统变量中。

在win10搜索框输入 envi:

然后点击上面的结果:

点击环境变量:

这里面上边是用户的变量,下面是系统的变量,这就是刚才EnvironmentVariableConfigurationProvider里面调试出来的那一堆环境变量。

而这个地方就是在你应该服务器上添加连接字符串的地方。再看一下调试:

Environment的Provider在第4个位置,appSettings.production.json的在第3个位置。也就是说如果appSettings.Product.json和系统环境变量都有一样Key的连接字符串,那么程序会选择系统环境变量的值,因为它是后边的配置会覆盖前边的配置。

在系统环境变量中添加:

然后调试运行(需要重启VS,以便新添加的系统环境变量生效):

嗯,没问题!

种子数据 Seed Data

目前EF Core还没有内置的方法来做种子数据。那么自己写:

建立一个MyContextExtensions.cs:

namespace CoreBackend.Api.Entities
{    public static class MyContextExtensions{        public static void EnsureSeedDataForContext(this MyContext context){            if (context.Products.Any()){                return;}            var products = new List<Product>{                new Product{Name = "牛奶",Price = 2.5f,Description = "这是牛奶啊"},                new Product{Name = "面包",Price = 4.5f,Description = "这是面包啊"},                new Product{Name = "啤酒",Price = 7.5f,Description = "这是啤酒啊"}};context.Products.AddRange(products);context.SaveChanges();}}
}

这是个Extension method,如果数据库没有数据,那就弄点种子数据,AddRange可以添加批量数据到Context(被Context追踪),但是到这还没有插入到数据库。使用SaveChanges会把数据保存到数据库。

然后再Startup的Configure方法中调用这个method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,            MyContext myContext){            // loggerFactory.AddProvider(new NLogLoggerProvider());            loggerFactory.AddNLog();            if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}            else{app.UseExceptionHandler();}            myContext.EnsureSeedDataForContext();app.UseStatusCodePages();app.UseMvc();}

首先注入MyContext,然后调用这个extension method。

然后把系统环境变量中的连接字符串删了把,并且把项目属性Debug中改成Development,这时候需要重启VS,因为一般环境变量是在软件启动的时候附加到其内存的,软件没关的情况下如果把系统环境变量给删了,在软件的内存中应该还是能找到该环境变量,所以软件得重启才能获取最新的环境变量们。重启VS,并运行:

种子数据进去了!

相关文章: 

  • 从头编写 asp.net core 2.0 web api 基础框架 (1)

  • 从头编写 asp.net core 2.0 web api 基础框架 (2)

  • 从头编写 asp.net core 2.0 web api 基础框架 (3)

原文地址: http://www.cnblogs.com/cgzl/p/7661805.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

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

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

相关文章

十大网站推荐

在b站上面看到个视频推荐的网站&#xff0c;感觉质量不错&#xff0c;先记下来&#xff0c;方便以后自己用到的时候来查 1.视频片头动画模板 https://panzoid.com/ 2.在线抠图 https://www.remove.bg/ 3.字体下载 https://www.zitijia.com/ 4. p站 &#xff08;二次元插画网站…

nssl1249-C【数论】

正题 题目大意 求 ∑a1n∑b1a(gcd(a,b)axorb)\sum_{a1}^n\sum_{b1}^a(gcd(a,b)a\ xor\ b)a1∑n​b1∑a​(gcd(a,b)a xor b) 解题思路 因为ababab时肯定不成立&#xff0c;所以直接计算a>ba>ba>b 那么gcd(a,b)⩽a−bgcd(a,b)\leqslant a-bgcd(a,b)⩽a−b&#xff0c;…

顺序表基本操作在主函数中的实现

#include<iostream> #define OK 1 #define ERROR 0 #define OVERFLOW -2 #define MAXSIZE 100 using namespace std;typedef int Status;typedef struct {int *elem;int length; } SqList; //构建空线性表 Status InistList(SqList &L) {L.elemnew int [MAXSIZE];if(…

spring cloud+dotnet core搭建微服务架构:配置中心(四)

前言 我们项目中有很多需要配置的地方&#xff0c;最常见的就是各种服务URL地址&#xff0c;这些地址针对不同的运行环境还不一样&#xff0c;不管和打包还是部署都麻烦&#xff0c;需要非常的小心。一般配置都是存储到配置文件里面&#xff0c;不管多小的配置变动&#xff0c;…

从零开始用好 Maven : 从 Hello World 到日常使用

转载自 从零开始用好 Maven : 从 Hello World 到日常使用 1. Maven简介 Apache Maven 是一个软件项目管理工具。基于项目对象模型&#xff08;POM&#xff09;的理念&#xff0c;通过一段核心描述信息来管理项目构建、报告和文档信息。 Maven 是一个意第绪语&#xff08;犹…

nssl1248-B【点分治,平衡树】

正题 题目大意 有一颗树&#xff0c;求一条路径长度k&#xff0c;要求S≤k≤ES\leq k\leq ES≤k≤E&#xff0c;求最小的k。 解题思路 其实对于每个点进行点分治&#xff0c;每次将整棵子树的路径加入平衡树&#xff0c;然后在统计一次答案。时间复杂度O(n2)O(n^2)O(n2)。 之…

单链表基本操作在主函数中的实现

#include <iostream> #define OK 1 #define ERROR 0 #define VOERFLOE -2 using namespace std;typedef int Status; typedef struct LNode {int date;struct LNode *next; } LNode,*LinkList;//构造一个空的单链表 Status InitList(LinkList &L) {Lnew LNode;L->…

.NET Core和.NET Standard有什么不同

近日&#xff0c;微软发布了.NET Core 2.0&#xff0c;但是开发人员中间仍然存在一些疑惑&#xff0c;就是.NET Core、.NET Standard、Xamarin和.NET Framework有什么不同。 .NET Framework用于构建桌面应用程序和运行在互联网信息服务器&#xff08;IIS&#xff09;上的ASP.NE…

十大经典排序算法

转载自 十大经典排序算法 内容几乎完全来源于网络&#xff0c;整理人&#xff1a;hustcc 来源&#xff1a;https://github.com/hustcc/JS-Sorting-Algorithm 排序算法是《数据结构与算法》中最基本的算法之一。 排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据…

将网页打包app

准备&#xff1a;Hbuilder X 一个网址 打开软件&#xff0c;选择新建 新建项目 再里面写上名称和地址 这里给个免费看电影的网站 http://www.k2938.com/ 在manifest.json配置相关文件&#xff0c;如图标 启动图也可以设置 最后发行 取消ios&#xff0c;使用DCloud证书…

ssl提高组周一备考赛【2018.10.29】

前言 想去德育基地… 成绩 RankRankRankPersonPersonPersonScoreScoreScoreAAABBBCCC1112017myself2017myself2017myself2202202201001001006060606060602222017lrz2017lrz2017lrz2102102101001001001010101001001003332017xxy2017xxy2017xxy20020020010010010070707030303044…

DotNetCore跨平台~配置文件与配置代码如何共存

古人云《一山不容二虎》&#xff0c;而进行dotnet core时代之后&#xff0c;我们可以看到这样的一些官方的DEMO&#xff0c;它将数据连接串和其它配置项都直接硬编码在代码里&#xff0c;即在startup中进行定义&#xff0c;试问你在生产环境如何兼容&#xff01;当然&#xff0…

将数组前n个和后m-n个整体逆置的实现

#include <iostream>using namespace std;/*void inverst(int *R,int s,int t){int k,w;for(ks;k<(st)/2;k){wR[k];R[k]R[t-ks];R[t-ks]w;} } int main() {int A[10],i,m;cout<<"请输入10个数&#xff1a;";for(i0;i<10;i){cin>>A[i];}cout&…

高效实用的.NET开源项目

似乎...很久很久没有写博客了&#xff0c;一直都想写两篇&#xff0c;但是却没有时间写。感觉最近有很多事情需要处理&#xff0c;一直都是疲于奔命&#xff0c;一直到最近才变得有些时间学习和充电。最近没有事情都会看一些博客和开源项目&#xff0c;发现介绍开源项目的文章似…

nssl1254-A(林下风气)【树形dp】

正题 题目大意 求一棵树上有多少个联通块的最大值和最小值差为k。 解题思路 其实直接用差<k的减去差<k的就是等于k的答案。 然后枚举一个点为最大值&#xff0c;然后只往小编号扩张就好了(不重)。 code #include<cstdio> #include<cstring> #include<a…

JVM(Java虚拟机)优化大全和案例实战

转载自 JVM&#xff08;Java虚拟机&#xff09;优化大全和案例实战 堆内存设置 原理 JVM堆内存分为2块&#xff1a;Permanent Space 和 Heap Space。 Permanent 即 持久代&#xff08;Permanent Generation&#xff09;&#xff0c;主要存放的是Java类定义信息&#xff0c;与…

对ASCALL码的理解

因为计算机只能识别0和1&#xff0c;相当于一个二进制&#xff0c;即位 程序中8个位构成一个字节&#xff0c;这样总共 2的八次方是等于 256 而早期美国的程序员只对0-128进行了编码 第0&#xff5e;32号及第127号(共34个)是控制字符或通讯专用字符&#xff0c;如控制符&#…

nssl1255-B(轻功)【SPFA,分层图】

正题 题目大意 有k中轻功&#xff0c;n个木桩&#xff0c;每种轻功可以消耗wisw_i\ swi​ s飞过aia_iai​个木桩&#xff0c;有些木桩有不可以被某种轻功飞过的限制&#xff0c;然后切换一次轻功要WsW\ sW s 解题思路 将图分成kkk层&#xff0c;每层表示在不同的轻功状态&…

EFCore废弃了TransactionScope取而代之的Context.Database.BeginTransaction

TransactionScope是.net平台基于的分布式事务组件&#xff0c;它默认为本地事务&#xff0c;同时当系统有需要时可以自动提升为分布式事务&#xff0c;而对系统的前提是要开启MSDTC服务&#xff0c;必要时需要在数据库服务器与应用服务器之间添加hosts的映射&#xff0c;这些在…

从 Spring Cloud 看一个微服务框架的「五脏六腑」

转载自 从 Spring Cloud 看一个微服务框架的「五脏六腑」 Spring Cloud 是一个基于 Spring Boot 实现的微服务框架&#xff0c;它包含了实现微服务架构所需的各种组件。 注&#xff1a;Spring Boot 简单理解就是简化 Spring 项目的搭建、配置、组合的框架。因为与构建微服务本…