当前位置: 首页 > news >正文

wordpress网站乱码品牌app定制

wordpress网站乱码,品牌app定制,常用ppt模板网站,wordpress文章相册模式背景知识 相关术语 .Net .NET是微软的一个开发平台#xff0c;这个平台的一大特点就是跨语言性#xff0c;不管是什么语言#xff0c;c、c、c#、F#、J#、vb等语言都可以用这个平台合作开发#xff1b; .NET#xff0c;它是微软创建的一个用于构建多种不同类型的应用程… 背景知识 相关术语 .Net .NET是微软的一个开发平台这个平台的一大特点就是跨语言性不管是什么语言c、c、c#、F#、J#、vb等语言都可以用这个平台合作开发 .NET它是微软创建的一个用于构建多种不同类型的应用程序的开发人员平台。 .NET 是一个广泛的术语用于描述整个 Microsoft 的软件开发生态系统。它包括了多种不同的技术和工具用于构建不同类型的应用程序包括 Windows 应用程序、Web 应用程序、移动应用程序等。 在过去几年中所说的.NET主要是指向的是.NET Framework,它是.NET简称的最早拥有者有着近20年的历史开发的软件只能在Windows系统下运行。而在这两年中人们所说的.NET又慢慢指向了.NET Core。 .Net 和Java有何不同? .NET是个开发平台Java是一种编程语言。 两者没啥可比性硬要比的话应该是C#和Java比。 不过平时口头上有时候会把C#和.NET等同起来确实会出现拿这两者作比较这个时候通常是讲.NET相关技术和Java相关技术比较。 因为两者确实有很多对应的技术比如.NET有ASPJava有JSP桌面端.NET有WinForm、WPFJava有swing、Javafx等。当然它们各有千秋有些技术不适应环境已经快被淘汰了。 总之我认为两者各有千秋C#在工业领域应用广Java在企业应用广。从当下国内大环境来讲Java是热于C#的不过这不是C#的问题是生态、历史等多因素造成的。同时Java求职市场也更卷国内大厂应用多。 .NET Framework .NET Framework 是最初的 .NET 实现主要用于 Windows 平台上的应用程序开发,并且依赖于特定的 Windows 版本。它提供了大量的类库支持 Web、桌面、移动端等各种应用程序的开发。但由于它只能在 Windows 平台上运行所以在跨平台方面存在局限性。 .NET Core .NET Core 是 .NET 的全新实现旨在解决 .NET Framework 的跨平台问题它可以在 Windows、Mac 和 Linux 等操作系统上运行。.NET Core 具有模块化和轻量级的特性适合用于云计算和容器这样的环境。 支持独立部署不会存在互相影响的问题彻底模块化运行效率高不依赖于IIS跨平台符合现代开发理念依赖注入单元测试等 ​ .NetCore版本发布 **时间 ****版本 **备注2016年2月.Net Core 1.0 RC12016年5月.Net Core 1.0 RC22016年6月.Net Core 1.02017年3月.Net Core 1.12017年8月.Net Core 2.02018年5月.Net Core 2.1(LTS)长期支持版本2018年12月.Net Core 2.22019年9月.Net Core 3.0(Maintenance)2019年12月.Net Core 3.1(LTS)长期支持版本2020年11月.NET5.02021年11月.NET6.0(LTS)长期支持版本2022年11月.NET7.02023年11月.NET8.0(LTS)长期支持版本 其中LTS版本是长期支持版本相对比较推荐 目前3.1是推荐使用版本后续微软计划每一年发布一个版本其中偶数版本为LTS版本 因为.Net Core是跨平台的每一个ASP.NET Core 应用程序从本质上来说都是一个独立的控制台应用并不需要依托IIS(web服务器)来运行这也是它能实现跨平台的一个基础。 可以这么类比,之前的.Net FrameWork部署在IIS服务器类似于Java之前的SSM阶段部署在Tomcat服务器中。 而现在的.Net Core不用依托IIS服务器类似于现在Java的SpringBoot阶段内嵌了服务器,所以可以单独启动。 .Net Core SDK SDK 包含构建和运行.NET Core 应用程序所需的一切。 .Net Core Runtime .NET Core Runtime已经包含在 SDK 中。因此如果您已安装 SDK则无需安装 .NET Core Runtime.NET Core Runtime 仅包含运行现有.NET Core 应用程序所需的资源。 .NET Standard .NET Standard 不是一个实际的 .NET 运行时实现而是定义了一组 API这些 API 是所有 .NET 实现都需要支持的。通过对 .NET Standard 的支持开发者可以编写一次代码并在所有 .NET 平台上运行提高了代码的复用性。 .NET 5 .NET 5包括 .NET 5、.NET 6 等 是 .NET Core 的后续版本它将 .NET Framework 和 .NET Core 合并为一个统一的平台。 即从NET5.0版本开始将不再区分.NetFrameWork和.NetCore统一叫.NET之前之所以叫.NetCore应该就是为了区分两个版本 这意味着现在有一个统一的 .NET 平台可以用于构建跨平台应用程序而不再需要选择 .NET Framework 或 .NET Core。 .NET 5 支持更多的应用类型和平台包括桌面应用、Web 应用、云服务、移动设备、游戏、物联网,提供了更好的性能和生产力。 从.NET 5开始微软开始淡化其他叫法统一为.NET 后续默认.NET指的就是.NET Core. Asp.Net ASP.NET 是专门用于构建 Web 应用程序的子集。ASP.NET 应用程序运行在 .NET 运行时之上。ASP.NET 主要关注 Web 开发提供了专门的工具和框架来简化 Web 应用程序的开发和部署。 Asp.Net Core ASP.NET Core 是 .NET Core 的一个组件专门用于构建 Web 应用程序和服务。ASP.NET Core 构建在 .NET Core 之上利用了 .NET Core 提供的跨平台性和性能优势。总的来说.NET Core 提供了一个通用的开发平台而 ASP.NET Core 则是在此基础上专注于 Web 开发提供了丰富的 Web 开发功能和工具. 对于ASP.NET Core应用程序来说我们要记住非常重要的一点是 ASP.NET Core应用程序拥有一个内置的Self-Hosted自托管的Web ServerWeb服务器用来处理外部请求。 不管是托管还是自托管都离不开Host宿主。 在ASP.NET Core应用中通过配置并启动一个Host来完成应用程序的启动和其生命周期的管理。而Host的主要的职责就是Web Server的配置和**Pilpeline请求处理管道**的构建。 ASP.NET Core应用程序的启动主要包含三个步骤1.CreateDefaultBuilder()创建IWebHostBuilder2.Build()IWebHostBuilder负责创建IWebHost3.Run()启动IWebHost所以ASP.NET Core应用的启动本质上是启动作为宿主的WebHost宿主对象。ASP.NET和ASP.NET Core都是由Microsoft开发的在.NET平台上运行的框架用于开发Web应用程序。以下是他们之间的一些主要区别和联系 平台兼容性ASP.NET仅运行在Windows平台上而ASP.NET Core是跨平台的可在Windows、Linux和macOS上运行。这使得ASP.NET Core更具灵活性和广泛的应用可能性。性能ASP.NET Core被设计为更高效性能常常超过传统的ASP.NET。这主要是由于ASP.NET Core的模块化设计和优化。模块化和微服务ASP.NET Core具有模块化的框架设计这使得ASP.NET Core更加轻便和灵活架构的应用。而ASP.NET没有这样的设计。版本依赖ASP.NET Core的一个重要特点是侧重于应用级别的依赖这意味着不同的ASP.NET Core应用可以运行在同一系统上而不会互相影响。而ASP.NET依赖于系统级别的.NET Framework版本。集成性ASP.NET Core通过内置的依赖注入、中间件配置来提供更大的控制和更好的集成性。而ASP.NET则需要依赖第三方库 like Unity, Ninject等来实现依赖注入。API的统一性ASP.NET分为Web API和MVC两个部分他们有各自独立的类库和命名空间。而在ASP.NET Core中Web API和MVC是统一起来的在构建RESTful服务时会更加方便。ASP.Net Core可以托管在更多的地方:如IIS, Apache,Docker,自托管等。 在联系方面两者都是用于开发Web应用程序的框架并且都使用.NET技术。代码语法、设计原则、编程模式有很多相似之处对于ASP.NET的开发者来说转到ASP.NET Core会更容易。 归根结底选择ASP.NET还是ASP.NET Core取决于项目需求平台依赖性能需求和个人或团队的技能水平。 程序集和库 .dll 或 .exe 文件其中包含一组可由应用程序或其他程序集调用的 API 。 程序集可以包括接口、类、结构、枚举和委托等类型。 有时项目的 bin 文件夹中的程序集被称为二进制文件。 而库或框架是由应用或其他库调用的 API 集合。 .NET 库由一个或多个程序集组成。库和框架通常作同义词使用。 什么是CLR和IL .NET CLRCommon Language Runtime是.NET的核心组成部分它为.NET应用程序提供了一个运行环境。 ILIntermediate Language是.NET Framework中的一种中间语言也被称为CIL (Common Intermediate Language) 或 MSIL (Microsoft Intermediate Language)。 在.NET环境中所有的.NET代码无论它是由C#、VB.NET还是其他.NET支持的语言编写的在编译时都首先被转换为IL。IL是一种面向堆栈的计算机指令集设计用于由具有高级语言特性的编程语言生成并可进一步由JITJust-In-Time编译器在运行时转换为本地代码。 这个过程中的几个关键点包括 平台独立性通过编译成IL.NET应用程序可以在任何平台上运行只要该平台有.NET运行环境即可。语言互通性由于所有.NET语言都编译到统一的IL因此不同语言编写的代码可以轻松地进行交互。优化在运行时CLR的JIT编译器可以根据目标计算机的具体硬件进行优化以提高应用程序的性能。 一个.NET CLR结构的图片通常会包括以下部分 最上层是各种**.NET应用程序**它们是由各种.NET语言如C#、VB.NET、F#等编写的。 这些应用程序在运行时都依赖于下一层的Common Language Runtime (CLR)。CLR是运行所有.NET代码的环境。 在CLR之下可能会有几个子组件例如 JIT编译器负责将中间语言CIL转换成特定平台上的机器语言。垃圾收集器负责自动管理内存回收不再使用的对象。安全组件负责执行访问检查和权限验证等安全操作。Type Checker负责确保类型的正确性和安全性。 最底层是操作系统所有的.NET应用程序和CLR最终都运行在这个操作系统上。 注意: .NET框架的CLR和Java虚拟机在某种程度上可以进行比较。 Java中的虚拟机Java Virtual Machine - JVM是Java平台的核心组件它负责解释和执行Java字节码。JVM允许Java程序在不同的操作系统上运行并提供了垃圾回收、内存管理和安全性等功能。 类似地.NET框架也提供了类似的功能。 .NET框架有一个称为Common Language RuntimeCLR的组件它负责执行和管理.NET应用程序。CLR解释和执行ILIntermediate Language代码这是.NET应用程序编译后生成的中间语言。 因此从某种程度上说.NET框架中的CLR可以类比于Java中的虚拟机。两者都负责解释和执行中间代码并提供了一些共享功能如垃圾回收和安全性。 然而请注意Java虚拟机和.NET框架之间仍存在一些区别。它们使用不同的中间语言Java字节码和IL代码和不同的运行时环境。此外Java平台和.NET平台也具有不同的生态系统和工具链。 综上所述虽然.NET框架的CLR在某种程度上可以类比于Java中的虚拟机但它们仍有一些差异。每个技术栈都有各自的特点和用途因此选择使用哪种取决于项目需求和开发团队的首选项。 asp.net core启动流程解析 一个WebAPI项目启动方式本质也是一个控制台程序程序入口都是从Main函数开始就从里面方法看看大概都做了什么其中择取几个方法源码简要说明主要功能。 1. Host.CreateDefaultBuilder方法: public static IHostBuilder CreateDefaultBuilder(string[] args) {//实例化一个HostBuildervar builder new HostBuilder();//设置根目录builder.UseContentRoot(Directory.GetCurrentDirectory());//设置 Host相关配置的配置源builder.ConfigureHostConfiguration(config {//从环境变量中获取前缀名为DOTNET_config.AddEnvironmentVariables(prefix: DOTNET_);//如果命令行中有参数可从命令行中读取if (args ! null){config.AddCommandLine(args);}});//设置应用程序配置的配置源builder.ConfigureAppConfiguration((hostingContext, config) {var env hostingContext.HostingEnvironment;//根据运行环境加载不同的配置文件并开启了热更新config.AddJsonFile(appsettings.json, optional: true, reloadOnChange: true).AddJsonFile($appsettings.{env.EnvironmentName}.json, optional: true, reloadOnChange: true);if (env.IsDevelopment() !string.IsNullOrEmpty(env.ApplicationName)){var appAssembly Assembly.Load(new AssemblyName(env.ApplicationName));if (appAssembly ! null){config.AddUserSecrets(appAssembly, optional: true);}}//可从环境变量中获取config.AddEnvironmentVariables();//如果命令行中有参数可从命令行中读取if (args ! null){config.AddCommandLine(args);}})//配置日志显示.ConfigureLogging((hostingContext, logging) {//判断操作系统var isWindows RuntimeInformation.IsOSPlatform(OSPlatform.Windows);//如果是Windows系统配置对应的显示级别//IMPORTANT: This needs to be added *before* configuration is loaded, this lets//the defaults be overridden by the configuration.if (isWindows){// Default the EventLogLoggerProvider to warning or abovelogging.AddFilterEventLogLoggerProvider(level level LogLevel.Warning);}//获取配置文件中Logging 段相关的配置信息添加到日志配置中logging.AddConfiguration(hostingContext.Configuration.GetSection(Logging));//在控制台输出所以启动程序的时候就看见输出日志logging.AddConsole();//在Debug窗口输出logging.AddDebug();logging.AddEventSourceLogger();//如果是Windows系统可以在系统日志中输出日志if (isWindows){// Add the EventLogLoggerProvider on windows machineslogging.AddEventLog();}})//使用默认的依赖注入容器.UseDefaultServiceProvider((context, options) {var isDevelopment context.HostingEnvironment.IsDevelopment();options.ValidateScopes isDevelopment;options.ValidateOnBuild isDevelopment;});return builder; }2. ConfigureWebHostDefaults 方法: public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, ActionIWebHostBuilder configure) {return builder.ConfigureWebHost(webHostBuilder {//指定是用的服务器及集成一些默认管道WebHost.ConfigureWebDefaults(webHostBuilder);//调用传入的委托这里是外部指定Startup类做服务注册和管道配置configure(webHostBuilder);}); }2.1 WebHost.ConfigureWebDefaults方法: internal static void ConfigureWebDefaults(IWebHostBuilder builder) {builder.ConfigureAppConfiguration((ctx, cb) {if (ctx.HostingEnvironment.IsDevelopment()){//静态文件环境的配置启用StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration);}});//指定Kestrel作为默认的Web服务器builder.UseKestrel((builderContext, options) {options.Configure(builderContext.Configuration.GetSection(Kestrel));})// 服务中间的注册包含路的中间件注册.ConfigureServices((hostingContext, services) {// 针对配置节点AllowedHosts改变时的回调// Fallbackservices.PostConfigureHostFilteringOptions(options {if (options.AllowedHosts null || options.AllowedHosts.Count 0){// AllowedHosts: localhost;127.0.0.1;[::1]var hosts hostingContext.Configuration[AllowedHosts]?.Split(new[] { ; }, StringSplitOptions.RemoveEmptyEntries);// Fall back to * to disable.options.AllowedHosts (hosts?.Length 0 ? hosts : new[] { * });}});//对应配置改变时触发通知// Change notificationservices.AddSingletonIOptionsChangeTokenSourceHostFilteringOptions(new ConfigurationChangeTokenSourceHostFilteringOptions(hostingContext.Configuration));services.AddTransientIStartupFilter, HostFilteringStartupFilter();if (string.Equals(true, hostingContext.Configuration[ForwardedHeaders_Enabled], StringComparison.OrdinalIgnoreCase)){services.ConfigureForwardedHeadersOptions(options {options.ForwardedHeaders ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;// Only loopback proxies are allowed by default. Clear that restriction because forwarders are// being enabled by explicit configuration.options.KnownNetworks.Clear();options.KnownProxies.Clear();});services.AddTransientIStartupFilter, ForwardedHeadersStartupFilter();}services.AddRouting();})//对使用IIS相关中间件.UseIIS().UseIISIntegration(); }3. Build方法其实这个方法就是根据之前配置构造出一个IHost对象: public IHost Build() {if (_hostBuilt){throw new InvalidOperationException(Build can only be called once.);}_hostBuilt true;//执行ConfigureHostConfiguration添加的一系列配置回调方法BuildHostConfiguration();//运行环境相关创建如ApplicationName、EnvironmentName、ContentRootPath等CreateHostingEnvironment();//构建HostBuilderCreateHostBuilderContext();//执行ConfigureAppConfigureation添加的一系列配置回调方法BuildAppConfiguration();//注入默认服务如IHost、ILogging等执行ConfigureServices添加的一系列回调方法CreateServiceProvider();return _appServices.GetRequiredServiceIHost(); }4. Run()方法开启服务器之后就可以进行请求了: 综上几个关键方法从其中Host这个关键词出现很多次 其实在Asp.Net Core应用中是通过配置并启动一个Host来完成应用程序的启动和生命周期的管理。 而Host主要就是对Web Server的配置和请求处理管理的管理 简要流程如下图 NuGet(包管理) VS的图形界面方式 找到工具 NuGet包管理器 管理解决方案的NuGet程序包 按需下载可以选择版本在“浏览”中搜索自己所需要的包然后下载即可同时可以查看第三方软件包的相关信息。 CLI方式(命令行接口方式) 更新或卸载程序包 安装: Install-Package XXX卸载Uninstall-Package XXX更新Updata-Package XXX 一般情况下CLI方式默认安装的将会是最新版本的软件包但是可以根据自己需求调整包的版本。 步骤如下 在NuGte官网上找到自己想要获取的第三方软件包的详情页面在Package Manager中复制命令 打开NuGet程序包管理器控制台 在控制台输入复制的命令即可完成程序包的下载。同时在控制台中还可选择需要引入程序包的项目 项目结构 了解项目结构和启动文件和启动顺序 带有这种网络地球图标的才是web项目目录。 wwwroot网站的静态文件目录。 此目录存放项目中所有公共的、静态的资源如 css、js 和 图片文件。 如果不做额外的配置那么 wwwroot 将是应用程序中唯一可以存放静态文件的位置. 如果没有此目录,需要手动在web项目目录下新建个wwwroot文件夹新建完文件夹后若此文件夹的图标没有变化的话,则需在vs中重新打开解决方案文件即可。 Controllers手动创建存放各种控制器Controllers。 appsettings.json配置文件比如数据库连接字符串等配置信息。 用途appsettings.json 主要用于存储应用程序的配置数据例如数据库连接字符串、日志设置、应用程序的行为配置等。 这些配置数据通常是应用程序在运行时需要读取的信息。 .Net Core中有个IConfiguration类用于读取配置文件 内容appsettings.json 文件通常包含一个JSON格式的配置对象其中包括键值对用于指定各种应用程序设置。 环境特定设置通常appsettings.json 文件可以有多个版本每个版本对应于不同的应用程序环境例如开发、测试和生产环境。这些环境特定的配置文件可以命名为appsettings.Development.json、appsettings.Production.json等以便在不同环境中使用不同的配置。 Program.cs程序入口文件. 主应用程序类包含了Main主函数来运行应用程序 ASP.NET Core应用程序需要由**Host宿主**进行管理宿主为其提供运行环境并负责启动。 所以Main函数主要是用来初始化宿主环境而宿主环境的初始化需要借助WebHostBuilder。 初始化完毕后调用Run()方法来启动应用程序。 这个Main()方法配置 ASP.NET Core 并启动它此时它成为一个 ASP.NET Core Web 应用程序。 因此如果你跟踪一下Main()方法它会调用 CreateWebHostBuilder()方法传递命令行参数。 然后你就可以看到CreateWebHostBuilder()方法返回一个实现 IWebHostBuilder 的对象。 在此对象上调用Build()方法会将我们的 ASP.NET Core 应用程序生成并且托管到服务器上。 在服务器上的程序调用Run() 方法该方法运行Web 应用程序并开始侦听传入的 HTTP 请求。 CreateDefaultBuilder()方法执行多项操作来创建服务器. 设置Web服务器加载主机和应用程序的配置信息配置日志记录 Startup.cs此文件是 ASP.NET Core 项目的启动文件 Startup 类是 ASP.NET Core 应用程序启动时默认调用的类 其中定义了项目的配置和中间件。 Program.cs 和 Startup.cs 的区别在于 Program.cs 会调用 Startup.cs Program 类会实例化 Startup 类. 这个可以通过 Program.cs 中的代码看出来 WebHost.CreateDefaultBuilder(args).UseStartupStartup();除了 Program.cs 会调用 Startup.cs Program 类会实例化 Startup 类之外。但 Startup 能做的不仅仅是这些可以说 ASP.NET Core 中的各个组件和中间件都会和 Startup 类打交道。 Startup.cs 文件中有一个 Startup 类在这个类中可以配置应用程序甚至配置配置源。 Startup 类可以用来定义请求处理管道和配置应用程序需要的服务。 Startup类承担应用的启动任务所以按照约定起名为Startup不过你可以修改为任意类名强烈建议类名为Startup。 默认的Startup结构很简单包含 构造函数Configuration属性ConfigureServices方法Configure方法 【必需的】 Startup 类必须是公开的且必须包含以下两个方法: ConfigureServices() 方法 此方法用于定义应用程序所需要的服务例如 ASP.NET Core MVC 、 Entity Framework Core 和 Identity 等等。 常用的服务有部分服务框架已默认注册 AddControllers注册Controller相关服务内部调用了AddMvcCore、AddApiExplorer、AddAuthorization、AddCors、AddDataAnnotations、AddFormatterMappings等多个扩展方法AddOptions注册Options相关服务如IOptions、IOptionsSnapshot、IOptionsMonitor、IOptionsFactory、IOptionsMonitorCache等。很多服务都需要Options所以很多服务注册的扩展方法会在内部调用AddOptionsAddRouting注册路由相关服务如IInlineConstraintResolver、LinkGenerator、IConfigureOptionsRouteOptions、RoutePatternTransformer等AddAddLogging注册Logging相关服务如ILoggerFactory、ILogger、IConfigureOptionsLoggerFilterOptions等AddAuthentication注册身份认证相关服务以方便后续注册JwtBearer、Cookie等服务AddAuthorization注册用户授权相关服务AddMvc注册Mvc相关服务比如Controllers、Views、RazorPages等AddHealthChecks注册健康检查相关服务如HealthCheckService、IHostedService等 Configure() 方法 此方法用于定义请求管道中的中间件。也就是说Configure() 方法可以用来定义应用程序如何响应请求。如果希望应用程序的行为不同需要在 Configure() 方法中添加其他代码来更改请求管道。 ConfigureServices()方法配置应用程序所需的服务Configure()方法配置应用程序的请求处理管道 Startup.cs 文件中默认的内容如下: //默认的Startup结构很简单包含//构造函数 //Configuration属性 //ConfigureServices方法 //Configure方法using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection;namespace HelloWorld {public class Startup{// 该ConfigureServices方法在运行时被调用。// 可以使用该方法将服务添加到容器中//该方法在Configure方法之前被调用//该方法要么无参数要么只能有一个参数且类型必须为IServiceCollection//该方法内的代码大多是形如Add{Service}的扩展方法public void ConfigureServices(IServiceCollection services){}// 该Configure方法在运行时被调用// 可以使用该方法来配置 HTTP 请求管道//该方法是必须的,该方法用于配置HTTP请求管道通过向管道添加中间件应用不同的响应方式。//该方法在ConfigureServices方法之后被调用//该方法中的参数可以接受任何已注入到DI容器中的服务//该方法内的代码大多是形如Use{Middleware}的扩展方法//该方法内中间件的注册顺序与代码的书写顺序是一致的先注册的先执行后注册的后执行public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}//Run() 方法不经常见它是调用中间件的终端。//即:如果在 app.Run() 方法的后面继续注册另一个中间件那么注册的那个中间件永远不会被调用因为 Run() 方法是注册中间件的终端在它之后永远不会调用下一个中间件app.Run(async (context) {await context.Response.WriteAsync(Hello World!);});}} }依赖项管理项目所依赖的第三方组件的安装配置升级 Properties存放了一些 .json 文件用于配置 ASP.NET Core 项目 launchSettings.json: 启动配置文件为一个 ASP.NET Core 应用保存特有的配置标准用于应用的启动准备工作 用途launchSettings.json 用于配置项目的启动设置例如应用程序的启动方式、调试设置、环境变量等。 这个文件通常用于定义开发和调试时的应用程序行为。 内容launchSettings.json 文件包含一个JSON格式的对象其中包含了各种项目启动相关的配置如命令行参数、环境变量、启动浏览器、应用程序URL等。 该文件中出现的几个参数 commandName:指定要启动的Web服务器有三个可选值 Project启动 KestrelIISExpress启动IIS ExpressIIS不启用任何Web服务器使用IIS dotnetRunMessagesbool字符串指示当使用 dotnet run 命令时终端能够及时响应并输出消息launchBrowserbool值指示当程序启动后是否打开浏览器launchUrl默认启动路径applicationUrl应用程序Url列表多个URL之间使用分号;进行分隔。当launchBrowser为true时将{applicationUrl}/{launchUrl}作为浏览器默认访问的UrlenvironmentVariables环境变量集合在该集合内配置环境变量. 疑问appsetting.json文件和launchsetting.json文件的区别和联系? 联系 launchsettings.json 和 appsettings.json 都是用于配置应用程序的文件但它们的职责和用途不同。 使用场景 在开发过程中通常会同时使用这两个文件 launchsettings.json 用于配置调试器如何启动应用程序包括选择要启动的项目、使用的环境变量等。appsettings.json 用于存储应用程序的配置信息例如数据库连接字符串、日志级别等。 环境特定的配置 appsettings.json 文件可以根据不同的环境如开发、生产创建不同的版本如 appsettings.Development.json。这样可以在不同环境下使用不同的配置。 配置管理 appsettings.json 中的配置信息可以在应用程序中通过依赖注入或者 Configuration 对象来读取和使用。故appsetting,json文件有点类似于java中主要的核心配置文件。 .net core项目两种启动方式 参考链接: [控制台启动.Net Core 3.1 Web应用程序 项目 - 汪小让 - 博客园 (cnblogs.com)](https://www.cnblogs.com/wangxiaorang/p/14459423.html) 注意: .net core项目编译生成的文件夹,除了命令行用dotnet命令运行dll文件的方式可以启动项目。 还可以在项目编译生成的文件夹中直接运行相应的exe程序文件来启动项目。 [.Net Core 项目启动方式 - 贰拾~ - 博客园 (cnblogs.com)](https://www.cnblogs.com/zousc/p/12421131.html) 多环境配置 一款软件一般要经过需求分析、设计编码单元测试、集成测试以及系统测试等一系列测试流程验收最终上线。那么就至少需要4套环境来保证系统运行 Development开发环境用于开发人员在本地对应用进行调试运行Test测试环境用于测试人员对应用进行测试Staging预发布环境用于在正式上线之前对应用进行集成、测试和预览或用于验收Production生产环境应用的正式线上环境 环境配置方式 通过环境变量ASPNETCORE_ENVIRONMENT指定运行环境 注意如果未指定环境默认情况下为 Production 在项目的Properties文件夹里面有一个“launchSettings.json”文件该文件是用于配置VS中项目启动的。 .net core读取配置源数据 参考学习文章: 跟我一起学.NetCore之配置初体验 (qq.com) 跟我一起学.NetCore之自定义配置源-热更新-对象绑定 (qq.com) 项目文件(xx.csproj) 我们使用 C作为编程语言因此项目文件具有.csproj 扩展名。 .csproj 文件是 ASP.NET Core 项目的核心文件之一它包含了项目的配置信息和元数据。这个文件使用 XML 格式定义了项目的结构、依赖项、编译选项等。 作用 定义项目结构csproj 文件描述了项目中包含的文件、目录以及它们的相对路径。管理依赖项通过引入 NuGet 包或其他项目的引用来管理项目的依赖关系。配置编译选项可以指定编译器选项、目标框架等以控制项目的编译行为。构建和发布配置定义了如何构建和发布项目包括生成输出路径、发布目标等。 项目文件不包含任何文件夹或文件引用. 简单解释后的意思就是。在以前的 ASP.NET 中当我们使用解决方案资源管理器向项目添加文件或文件夹时项目文件中会包含对该文件或文件夹的引用。但是在 ASP.NET Core 中项目文件不包含任何文件夹或文件引用。 在解决方案中右键单击项目名称并选择编辑 xxxx.csproj 文件。 这将在编辑器中打开.csproj 文件。 Project SdkMicrosoft.NET.Sdk.WebPropertyGroupTargetFrameworknetcoreapp2.2/TargetFrameworkAspNetCoreHostingModelInProcess/AspNetCoreHostingModel/PropertyGroupItemGroupPackageReference IncludeMicrosoft.AspNetCore.App/PackageReference IncludeMicrosoft.AspNetCore.Razor.Design Version2.2.0 PrivateAssetsAll//ItemGroup /ProjectProject SdkMicrosoft.NET.Sdk.Web 指定了项目所使用的 SDKSoftware Development Kit这里使用了 ASP.NET Core 的 Web SDK。PropertyGroup 包含了一组属性用于配置项目的基本信息。 TargetFramework 定义了项目的目标 .NET Core/.NET 5 版本。AspNetCoreHostingModel 指定了 ASP.NET Core 的托管模型InProcess 或 OutOfProcess。RootNamespace 定义了项目的根命名空间。OutputPath 指定了项目编译输出的路径。 ItemGroup 用于管理项目的依赖项。 PackageReference 定义了项目引用的 NuGet 包。这里示例中引用了一些 ASP.NET Core 和 Entity Framework Core 的相关包。 中间件 .NET本身就是一个基于中间件middleware的框架它通过一系列的中间件组件来处理HTTP请求和响应。 一个管道可以有一个或多个中间件而中间件的职责是根据HttpContext处理HTTP请求然后往Response里填充东西最后完成整个Response的输出。 Startup 类中的 Configure() 方法用于定义请求管道中的中间件。 中间件控制我们的应用程序如何响应 HTTP 请求它还可以控制我们的应用程序在发生错误时的显示的内容它是我们认证和授权用户执行特定操作的关键部分。 中间件是一种装配到应用程序管道以处理请求和响应的组件。 每个中间件(组件) 可以选择是否将请求传递到请求管道中的下一个组件。可在调用管道中的下一个组件的前后执行工作。 请求管道中的每个中间件组件负责调用管道中的下一个组件或在适当情况下使链发生短路,从而避免不必要的工作。 真正理解起来很简单就是流水化的作业。 管道数据是如何流通的呢如下图所示: Request进入Middleware 1叠加一层处理的逻辑代码到HttpContext(切确说是HttpContext的Response对象)然后调用next()进入到下一个Middleware 2依次递推最后所有的逻辑代码叠加完毕后返回前端。 定义中间件: Startup.cs文件中的Configure()方法可以配置应用程序的请求处理管道以及定义中间件。 **定义: **修改 Startup 类中的 Configure() 方法: 往其中添加如下语句 app.Use某个中间件()即可。 注意app.run()是一个终端中间件,请求到达这里后就短路了,不会继续再调用下一个中间件。 **所以: **增加其他中间件的app.Usexx()语句需要放在app.run()方法前面此中间件才能定义成功, 因为app.run()是中间件定义的终点了,如果这个语句放在app.run()方法后面,那么定义这个中间件将不会生效。 常用的中间件 常用的中间件: app.UseDeveloperExceptionPage()这个中间件与其它中间件有些不同其它中间件通常会检查传入的请求并对该请求做出一些响应UseDeveloperExceptionPage中间件不关心传入的请求因为它总是在管道后发生,它会调用下一个中间件然后等待管道后面的任何事情是否会产生异常如果有异常这个中间件会给返回一个错误页面并显示有关该异常的详细信息. 我们还可以从 UseDeveloperExceptionPage 中间件返回的结果中看到原始异常详细信息. 所有这些信息对开发人员都非常有用 所以必须尽可能的在管道中提早注入。; app.Use() 注册的中间件会依次在请求管道中执行每个中间件都可以在请求到达时执行前置逻辑然后将请求传递给下一个中间件。 在 app.Use() 中间件中必须调用 await next() 才能将请求传递给下一个中间件。否则请求会在当前中间件中终止。 在中间件中如果已经开始给客户端发送Response请千万不要调用next.Invoke / next()也不要对Response进行任何更改否则将抛出异常。 app.Use() 用于注册处理中间件链中的中间件 app.Run() 通常被用作管道的最终处理程序,即是终端中间件它直接处理请求不会将请求传递给下一个中间件。 在 app.Run() 中间件中请求不会传递给下一个中间件它直接处理请求。app.Run() 通常用作最终处理程序直接处理请求。 app.UseStaticFiles() 中间件: 处理静态文件 注意: app.useStaticFiles中间件机制: 静态文件中间件执行的操作是针对给定的请求查看请求路径,然后将此请求路径与文件系统以及文件系统上的内容进行比较: 1.如果静态文件是一个可以使用的文件它将返回该文件而不会尝试调用下一个中间件(即不会继续往下走调用其他已定义的中间件) 2.如果它没有找到匹配的文件那么将继续调用下一个中间件(即继续往下走调用其他已定义的中间件) 3.浏览器上输入项目的根路径/,对应的是wwwroot文件夹 eg:浏览器上输入localhost:1215/images/demo.png 则对应的是wwwroot文件夹里的images文件夹中的demo.png 无论静态文件放在 wwwroot 的任何地方一级目录也好多级子目录也好任何 JavaScript 文件或 CSS 文件或 HTML 文件UseStaticFiles中间件都可以找到并返回它们 app.UseDefaultFiles()中间件: 指定默认首页打开的文件 app.UseDefaultFiles()中间件默认查找的文件名如下: index.htmlindex.htmdefault.htmldefault.htm 现在有个需求, 打开根路径/就直接匹配到 wwwroot 目录下的 index.html 文件 答:只需将定义app.UseDefaultFiles()中间件语句放在定义app.UseStaticFiles()中间件语句前面即可,如下: app.UseDefaultFiles(); app.UseStaticFiles(); 原理: 由上面app.UseStaticFiles中间件的知识可知,当其找到一个静态文件,它就直接返回该文件,不会继续调用下一个中间件。而app.UseDefaultFiles中间件用来指定默认文件,其放在app.UseStaticFiles中间件的前面,所以相当于用app.UseDefaultFiles先说明要指定默认文件,然后接着就调用app.UseStaticFiles中间件找到对应的静态文件. 总的来说,就相当于app.UseStaticFiles利用app.UseStaticFiles返回的静态文件作为其指定的默认文件列表。 app.UseFileServer()中间件 如果同时使用 UseDefaultFiles 和 UseStaticFiles 两个中间件很容易搞混它们之间的顺序 针对这种情况ASP.NET Core 提供了 UseFileServer 中间件这个中间件是对它们的封装。 使用UseFileServer中间件和同时使用 UseDefaultFiles UseStaticFiles 两个中间件时的效果一模一样。 app.UseMvc()中间件 使用 app.UseMvc() 方法时你需要手动配置所有的路由规则包括控制器、动作、参数等。 使用 app.UseMvc() 时你完全控制了路由的配置可以根据项目的需要进行高度定制。 app.UseMvc() 是在 Startup.cs 文件的 Configure 方法中调用的。它的作用是将 MVC 中间件添加到请求处理管道中以便它能够处理传入的请求并将其路由到相应的控制器和动作方法。 services.AddMvc() 注册了 MVC 相关的服务而 app.UseMvc() 将 MVC 中间件添加到请求处理管道中使其能够处理传入的请求。一般情况下会同时使用这两个方法来启用 MVC 功能. 总的来说services.AddMvc() 和 app.UseMvc() 一起协作为你的应用程序提供了处理 HTTP 请求的 MVC 功能。 然而从 ASP.NET Core 3.0 开始Microsoft 推荐使用终结点路由Endpoint Routing替代传统的 app.UseMvc()这使得配置更为灵活并且可以兼容传统的基于路由模板的配置方式。 app.UseMvcWithDefaultRoute()中间件 app.UseMvcWithDefaultRoute() 方法会添加一个默认的路由规则通常使用控制器名称、动作名称和参数来处理请求。 默认的路由规则是: {controllerHome}/{actionIndex}/{id?} 在大多数情况下特别是在典型的 web 应用程序中使用默认路由模板可以简化路由配置提高开发效率。 根据具体的项目需求来选择合适的配置方式。 在大多数情况下使用默认路由模板是一个不错的起点如果项目需要更高度的定制再考虑使用手动配置路由。 app.UseWhen:通过该方法针对不同的逻辑条件创建管道分支。 需要注意的是 进入了管道分支后如果管道分支不存在管道短路或终端中间件则会再次返回到主管道。 当使用PathString时路径必须以“/”开头且允许只有一个/字符 支持嵌套即UseWhen中嵌套UseWhen等 支持同时匹配多个段如 /get/user public class Startup {public void Configure(IApplicationBuilder app){// /get 或 /get/xxx 都会进入该管道分支app.UseWhen(context context.Request.Path.StartsWithSegments(/get), app {app.Use(async (context, next) {Console.WriteLine(UseWhen:Use);await next();});});app.Use(async (context, next) {Console.WriteLine(Use);await next();});app.Run(async context {Console.WriteLine(Run);await context.Response.WriteAsync(Hello World!);});} }app.Map:通过该方法针对不同的请求路径创建管道分支。需要注意的是 一旦进入了管道分支则不会再回到主管道。 使用该方法时会将匹配的路径从HttpRequest.Path 中删除并将其追加到HttpRequest.PathBase中。 路径必须以“/”开头且不能只有一个/字符 支持嵌套即Map中嵌套Map、MapWhen接下来会讲等 支持同时匹配多个段如 /post/user public class Startup {public void Configure(IApplicationBuilder app){// 访问 /get 时会进入该管道分支// 访问 /get/xxx 时会进入该管道分支app.Map(/get, app {app.Use(async (context, next) {Console.WriteLine(Map get: Use);Console.WriteLine($Request Path: {context.Request.Path}); Console.WriteLine($Request PathBase: {context.Request.PathBase});await next();});app.Run(async context {Console.WriteLine(Map get: Run);await context.Response.WriteAsync(Hello World!);});});// 访问 /post/user 时会进入该管道分支// 访问 /post/user/xxx 时会进入该管道分支app.Map(/post/user, app {// 访问 /post/user/student 时会进入该管道分支// 访问 /post/user/student/1 时会进入该管道分支app.Map(/student, app {app.Run(async context {Console.WriteLine(Map /post/user/student: Run);Console.WriteLine($Request Path: {context.Request.Path});Console.WriteLine($Request PathBase: {context.Request.PathBase});await context.Response.WriteAsync(Hello World!);});});app.Use(async (context, next) {Console.WriteLine(Map post/user: Use);Console.WriteLine($Request Path: {context.Request.Path});Console.WriteLine($Request PathBase: {context.Request.PathBase});await next();});app.Run(async context {Console.WriteLine(Map post/user: Run);await context.Response.WriteAsync(Hello World!);});});} }app.MapWhen:与Map类似只不过MapWhen不是基于路径而是基于逻辑条件创建管道分支。注意事项如下 一旦进入了管道分支则不会再回到主管道。 当使用PathString时路径必须以“/”开头且允许只有一个/字符 HttpRequest.Path和HttpRequest.PathBase不会像Map那样进行特别处理 支持嵌套即MapWhen中嵌套MapWhen、Map等 支持同时匹配多个段如 /get/user public class Startup {public void Configure(IApplicationBuilder app){// /get 或 /get/xxx 都会进入该管道分支app.MapWhen(context context.Request.Path.StartsWithSegments(/get), app {app.MapWhen(context context.Request.Path.ToString().Contains(user), app {app.Use(async (context, next) {Console.WriteLine(MapWhen get user: Use);await next();});});app.Use(async (context, next) {Console.WriteLine(MapWhen get: Use);await next();});app.Run(async context {Console.WriteLine(MapWhen get: Run);await context.Response.WriteAsync(Hello World!);});});} }Run用于注册终端中间件Use用来注册匿名中间件UseWhen、Map、MapWhen用于创建管道分支。UseWhen进入管道分支后如果管道分支中不存在短路或终端中间件则会返回到主管道。Map和MapWhen进入管道分支后无论如何都不会再返回到主管道。UseWhen和MapWhen基于逻辑条件来创建管道分支而Map基于请求路径来创建管道分支且会对HttpRequest.Path和HttpRequest.PathBase进行处理。 UseRouting路由中间件根据Url中的路径导航到对应的Endpoint。必须与UseEndpoints搭配使用。 UseEndpoints执行路由所选择的Endpoint对应的委托。 UseAuthentication身份认证中间件用于对请求用户的身份进行认证。比如早晨上班打卡时管理员认出你是公司员工那么才允许你进入公司。 UseAuthorization用户授权中间件用于对请求用户进行授权。比如虽然你是公司员工但是你是一名.NET开发工程师那么你只允许坐在.NET开发工程师区域的工位上而不能坐在老总的办公室里。 UseMvcMvc中间件。 UseHealthChecks健康检查中间件。 UseMiddleware用来添加匿名中间件的通过该方法可以方便的添加自定义中间件。 useAuthentication: 是ASP.NET Core中用于处理身份验证的中间件。它负责在请求处理过程中对用户进行身份验证以确保用户已经通过认证才能访问受保护的资源。 app.UseAuthentication 中间件用于启用身份验证处理在请求到达控制器之前对用户进行认证。它会检查请求的身份信息如Cookie或Bearer令牌并将已认证的用户信息附加到HttpContext.User属性中以便在后续的请求中使用。 要使用 app.UseAuthentication 中间件需要确保已经在应用程序中配置了身份验证服务例如使用ASP.NET Core Identity、OAuth、JWT等。 在 Startup.cs 文件中的 Configure 方法中将 app.UseAuthentication() 添加到中间件管道中: public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {// 其他中间件...app.UseAuthentication();// ... } UseAuthorization: app.UseAuthorization() 中间件是 ASP.NET Core 中用于处理授权的中间件。它负责在请求处理过程中对用户进行授权以确定用户是否有权访问特定的资源或执行特定的操作。 app.UseAuthorization() 中间件用于启用授权处理它会检查请求的用户是否被授权执行特定操作或访问特定资源。 要使用 app.UseAuthorization() 中间件需要确保已经在应用程序中配置了授权策略和规则。 在 Startup.cs 文件中的 Configure 方法中将 app.UseAuthorization() 添加到中间件管道中 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {// 其他中间件...app.UseAuthorization();// ... } 编写中间件 “约定大于配置”先来个约法三章 拥有公共public构造函数且该构造函数至少包含一个类型为RequestDelegate的参数 拥有名为Invoke或InvokeAsync的公共public方法必须包含一个类型为HttpContext的方法参数且该参数必须位于第一个参数的位置另外该方法必须返回Task类型。 构造函数 中的其他参数可以通过依赖注入DI填充也可以通过 UseMiddleware传参进行填充。 通过DI填充时只能接收 Transient 和 Singleton 的DI参数。这是由于中间件是在应用启动时构造的而不是按请求构造所以当出现 Scoped 参数时构造函数内的DI参数生命周期与其他不共享如果想要共享则必须将Scoped DI参数添加到Invoke/InvokeAsync来进行使用。通过UseMiddleware传参时构造函数内的DI参数和非DI参数顺序没有要求传入UseMiddleware内的参数顺序也没有要求但是我建议将非DI参数放到前面DI参数放到后面。这一块感觉微软做的好牛皮 Invoke/InvokeAsync的其他参数也能够通过依赖注入DI填充可以接收 Transient、Scoped 和 Singleton 的DI参数。 一个简单的中间件如下 public class MyMiddleware {// 用于调用管道中的下一个中间件private readonly RequestDelegate _next;public MyMiddleware(RequestDelegate next,//RequestDelegateITransientService transientService,ISingletonService singletonService){_next next;}public async Task InvokeAsync(//invoke方法,返回TaskHttpContext context,//contextITransientService transientService,IScopedService scopedService,ISingletonService singletonService){// 下一个中间件处理之前的操作Console.WriteLine(MyMiddleware Begin);await _next(context);// 下一个中间件处理完成后的操作Console.WriteLine(MyMiddleware End);} }然后你可以通过UseMiddleware方法将其添加到管道中 public class Startup {public void Configure(IApplicationBuilder app){app.UseMiddlewareMyMiddleware();} }基于工厂的中间件 优势 按照请求进行激活。这个就是说上面基于约定的中间件实例是单例的但是基于工厂的中间件可以在依赖注入时设置中间件实例的生命周期。使中间件强类型化因为其实现了接口IMiddleware 该方式的实现基于IMiddlewareFactory和IMiddleware。 先来看一下接口定义 public interface IMiddlewareFactory {IMiddleware? Create(Type middlewareType);void Release(IMiddleware middleware); }public interface IMiddleware {Task InvokeAsync(HttpContext context, RequestDelegate next); }当我们调用UseMiddleware时它是如何工作的呢事实上UseMiddleware扩展方法会先检查中间件是否实现了IMiddleware接口。 如果实现了则使用容器中注册的IMiddlewareFactory实例来解析该IMiddleware的实例这下你知道为什么称为“基于工厂的中间件”了吧。如果没实现那么就使用基于约定的中间件逻辑来激活中间件。 注意基于工厂的中间件在应用的服务容器中一般注册为 Scoped 或 Transient 服务。 示例实现一个基于工厂的中间件 public class YourMiddleware : IMiddleware {public async Task InvokeAsync(HttpContext context, RequestDelegate next){// 下一个中间件处理之前的操作Console.WriteLine(YourMiddleware Begin);await next(context);// 下一个中间件处理完成后的操作Console.WriteLine(YourMiddleware End);} }public static class AppMiddlewareApplicationBuilderExtensions {public static IApplicationBuilder UseYour(this IApplicationBuilder app) app.UseMiddlewareYourMiddleware(); }注意: 微软提供了IMiddlewareFactory接口的默认实现,所以咱们不用关心或者去手动书写IMiddlewareFactory接口的实现类。 然后在ConfigureServices中添加中间件依赖注入: public class Startup {public void ConfigureServices(IServiceCollection services){services.AddTransientYourMiddleware();} }最后在Configure中使用中间件: public class Startup {public void Configure(IApplicationBuilder app){app.UseYour();} }可以看到该工厂使用过DI容器来解析出服务实例的。因此当使用基于工厂的中间件时是无法通过UseMiddleware向中间件的构造函数传参的。 基于约定的中间件 VS 基于工厂的中间件 基于约定的中间件实例都是 Singleton而基于工厂的中间件实例可以是 Singleton、Scoped 和 Transient当然不建议注册为 Singleton基于约定的中间件实例构造函数中可以通过依赖注入传参也可以用过UseMiddleware传参而基于工厂的中间件只能通过依赖注入传参基于约定的中间件实例可以在Invoke/InvokeAsync中添加更多的依赖注入参数而基于工厂的中间件只能按照IMiddleware的接口定义进行实现。 服务 Startup.cs文件中的ConfigureServices()方法可以配置应用程序所需的服务。 配置MVC服务 services.AddMvc() public void ConfigureServices(IServiceCollection services) {services.AddMvc(); }全功能的 MVC 配置 AddMvc() 方法添加了完整的 MVC 功能包括视图引擎、模型绑定、过滤器、路由等。它提供了一套完整的 MVC 架构适用于大多数 web 应用程序。 便捷的配置 AddMvc() 方法提供了许多内置的默认配置可以在大多数情况下工作良好让你可以更快地启动一个新的 MVC 项目。 包括了许多默认组件 它默认包含了许多常用的组件比如 Razor 视图引擎、模型绑定、路由等使得你不必手动添加这些组件。 services.AddMvcCore() public void ConfigureServices(IServiceCollection services) {services.AddMvcCore(); }基础 MVC 配置 AddMvcCore() 方法添加了 MVC 的基本组件但没有包括一些高级功能比如 Razor 视图引擎默认模型绑定等。 更灵活的配置选项 AddMvcCore() 方法提供了更灵活的配置选项允许你手动选择添加你需要的组件和功能使得它更适合定制化和特定场景的需求。 轻量级 由于 AddMvcCore() 方法只添加了基本组件因此它相对于完整的 MVC 来说更加轻量级适用于一些对资源要求比较严格的场景。 如何选择 如果你正在创建一个传统的 web 应用程序并且你想使用 ASP.NET Core MVC 的全部功能那么 services.AddMvc() 是一个不错的选择。如果你正在构建一个特定场景下的 web API 或者单页应用SPA并且你想要对组件进行更精确的控制那么 services.AddMvcCore() 可能更适合你因为它允许你手动选择添加你需要的组件。 进程内托管和进程外托管 ASP.NET Core 支持两种主要的托管模型进程内托管In-Process Hosting和进程外托管Out-of-Process Hosting。 进程内托管部署In-Process Hosting 意思进程内托管就像把一个小队的所有成员都放在同一个房间里一样他们可以直接互相交谈。 特点 应用程序和服务器共用一个房间也就是同一个进程相当于在同一个大家庭里成员之间交流非常方便。在进程内托管模型中ASP.NET Core 应用程序直接运行在 IIS 或 IIS Express 进程中。这种模型通常用于小型应用程序或者在单个 IIS 站点中托管的情况。 优点 性能更高因为应用程序与 IIS 进程共享同一个进程所以它们可以直接相互通信不需要通过网络协议,通信效率高。资源占用少不需要额外的进程省内存。部署简单只需要将应用程序文件放置在指定目录即可不需要额外的配置。 缺点 隔离性差应用程序与 IIS 进程共享同一个进程如果应用程序崩溃可能会影响整个 IIS 进程。即如果其中一个成员出了问题可能会影响到其他成员。不适合多租户应用因为所有租户共享同一个进程难以实现真正的隔离。 进程外托管部署Out-of-Process Hosting 意思 进程外托管就像把一个小队的成员们分别安排在不同的房间里他们需要通过某种方式才能交谈。 特点 应用程序和服务器分开运行在不同的进程中就像住在不同的房间里。需要一些特殊的手段比如通过网络才能互相通信。 优点 更好的隔离性一个房间出了问题不会影响到其他房间。适用于大型、高流量的应用也可以提供更灵活的部署方式。 缺点 相对占用更多的系统资源因为需要多个进程。通信效率可能会稍低因为需要通过一些手段来进行交流。 总结: 性能和资源消耗进程内托管通常具有更高的性能和较低的资源消耗而进程外托管提供了更高的隔离性和灵活性。 选择根据你的应用程序的要求和特性来选择托管模型。如果你的应用程序比较小、资源要求不高可以选择进程内托管而对于大型或者需要更高隔离性的应用程序进程外托管可能更合适。 部署场景如果你需要将应用程序部署在特定的Web服务器之后或者通过反向代理等方式进行部署进程外托管是一个更好的选择。 总的来说进程内托管适用于小型应用程序或者资源要求不高的场景而进程外托管适用于大型、高流量的应用程序或者需要更灵活部署方式的情况。 IIS和IIS Express和Kestrel IIS和IIS Express的差别和联系: 功能差异 w3wp.exe 是 正式版本的IIS (Internet Information Services) 的工作进程用于托管和执行托管在 IIS 上的 ASP.NET 应用程序。 每个应用程序池Application Pool都会在 IIS 中启动一个 w3wp.exe 进程。这样可以隔离不同应用程序池的代码从而提高了系统的稳定性和安全性。 应用程序池提供了一个隔离的执行环境可以保证不同应用程序之间的互不干扰并且可以通过资源管理和安全性配置来优化服务器的性能和安全性。这使得 IIS 能够有效地托管多个应用程序并保持系统的稳定性。 IIS是一个全功能的Web服务器适用于生产环境和大规模应用。 IIS Express是一个精简版专注于本地开发和测试不适合用于生产环境。 IIS Express 并不直接使用 w3wp.exe。相反它自身包含了一个简化版的 Web 服务器来处理 HTTP 请求并提供与标准 IIS 类似的功能。 使用场景 IIS用于生产环境中的实际部署。IIS Express用于开发阶段的本地调试。 性能 由于IIS是一个全功能的Web服务器通常比IIS Express更强大能够处理更大的流量和并发请求。 部署要求 IIS需要在服务器操作系统上手动安装和配置。IIS Express通常会随着Visual Studio一起安装不需要额外配置。 总的来说IIS和IIS Express都是用于托管Web应用程序的工具但它们的主要区别在于使用场景和功能定位。IIS用于生产环境而IIS Express则是用于开发和测试阶段的本地调试工具。 IIS和Kestrel的区别和联系: 功能差异 w3wp.exe 是 正式版本的IIS (Internet Information Services) 的工作进程用于托管和执行托管在 IIS 上的 ASP.NET 应用程序。 每个应用程序池Application Pool都会在 IIS 中启动一个 w3wp.exe 进程。这样可以隔离不同应用程序池的代码从而提高了系统的稳定性和安全性。 应用程序池提供了一个隔离的执行环境可以保证不同应用程序之间的互不干扰并且可以通过资源管理和安全性配置来优化服务器的性能和安全性。这使得 IIS 能够有效地托管多个应用程序并保持系统的稳定性。 Kestrel 是 ASP.NET Core 的默认 Web 服务器与 IIS 不同它可以独立地运行也可以与反向代理如 IIS一起使用。 Kestrel 不使用 w3wp.exe 进程而是有自己的进程管理方式。 IIS 是一个全功能的 Web 服务器支持多种 Web 技术适用于大规模生产环境。 Kestrel 是一个轻量级的跨平台 Web 服务器专注于处理 HTTP 请求。 Kestrel它是 Microsoft Visual Studio 集成的一部分可以通过 Visual Studio 轻松启动 性能 IIS 在处理大量并发请求时通常比 Kestrel 更高效特别是在生产环境中。Kestrel 在轻负载情况下表现良好但在高负载情况下可能需要配合反向代理服务器使用。 部署要求 IIS 需要在 Windows 服务器操作系统上手动安装和配置。Kestrel 可以作为一个独立的服务器也可以与反向代理服务器结合使用。 适用场景 IIS 适用于大规模、高并发的生产环境支持多种 Web 技术。Kestrel 适用于开发和测试阶段的本地调试以及一些中小型应用程序的托管需求。 总的来说选择使用 IIS 还是 Kestrel 取决于你的项目需求和部署场景。通常在生产环境中会使用 IIS 作为反向代理服务器来托管 ASP.NET Core 应用以保证高性能和安全性。在开发和测试阶段可以直接使用 Kestrel 进行本地调试。 IIS Express和Kestrel的区别和联系: 功能差异 Kestrel 是 ASP.NET Core 的默认 Web 服务器与 IIS 不同它可以独立地运行也可以与反向代理如 IIS一起使用。 Kestrel 不使用 w3wp.exe 进程而是有自己的进程管理方式。 IIS Express 是一款用于本地调试的轻量级 Web 服务器适用于开发和测试阶段。 Kestrel 是一个跨平台的、轻量级的 Web 服务器可以用于开发、测试和生产环境。 性能 IIS Express 主要用于开发环境不强调高性能而是注重便于开发者进行调试。Kestrel 性能较好特别适合用于一些中小型应用程序和特定场景的高性能需求。 操作系统支持 IIS Express 只能在 Windows 上运行。Kestrel 可以在 Windows、Linux 和 macOS 上运行具有更强的跨平台能力。 使用场景 IIS Express 适用于开发阶段的本地调试为开发人员提供一个快速、方便的调试环境。Kestrel 可以用于各个阶段包括开发、测试和生产环境也可以与反向代理服务器结合使用提供高性能的托管解决方案。 总的来说选择使用 IIS Express 还是 Kestrel 取决于你的项目需求和开发环境。在开发阶段通常会使用 IIS Express 进行本地调试而在生产环境中通常会使用 Kestrel 作为主要的 Web 服务器。 Swagger相关 swagger基础参考的学习文章链接: 跟我一起学.NetCore之Swagger让前后端不再烦恼及界面自定义 (qq.com) Jwt(json web token)相关 参考的学习文章链接:跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt) (qq.com) 环境变量 在.NET Core中环境变量是一种配置机制用于在应用程序中访问外部配置信息或者控制应用程序的行为。 ASPNETCORE_ENVIRONMENT 用于指定应用程序的当前环境。 常见的值包括 Development开发环境、Staging预发布环境、Production生产环境等。 根据不同的环境应用程序可以采取不同的配置和行为。 Entity Framework和Entity Framework Core Entity FrameworkEF和Entity Framework CoreEF Core都是由微软开发的对象关系映射ORM框架用于将数据库中的数据映射到.NET应用程序中的对象模型。 Entity Framework / Entity Framework Core实体框架核心简称EFCore和LINQ语言集成查询的技术。使用这两者来实现数据访问。 数据持久化EF Core 提供了将对象持久化到数据库的能力包括插入、更新、删除等操作。LINQ 查询可以使用 LINQ (Language Integrated Query) 进行灵活的数据查询。数据库迁移可以自动追踪和应用模型变化更新数据库结构。 EF Entity FrameworkEF是一款由Microsoft支持的开源对象关系映射ORM框架它允许.NET开发人员在使用数据库时更加专注于业务逻辑减少输入大量SQL代码的工作量。 以往操作数据的话是用SQL语句对数据库的表进行操作 这是比较抽象的就感觉你操作的是存储在数据库中的数据。 而EF是以Entity Class为核心的也叫实体类你操作 的是该类的对象了从某种程度上来讲你操作的变成一个有数据体了。 而以数据体为核心的这一套数据访问框架称为实体框架。至于后来的EFCore纯粹是为了与.NET Core配对。 在数据和程序之间出现了一个模型(model). Model是由实体和上下文组成的。它不是一个类它是一套东西。 而其中与数据库交互的就是上下文。 EF支持的模型开发方式 从已有的数据库中生成model手工来编写一个匹配数据库的model一旦创建了一个模型使用EF Migrations来根据模型创建一个数据库。Migrations允许在模型更改时同时使数据库发生变化。 显然三种方式有各自的特点对于初学者来说应该先尝试第二种。 数据库已经建好通过手写一个model更能体会其中的一些机理。 DbContext及其派生类 DbContext 是 EF 和 EF Core 中非常重要的一个类它代表了一个数据库会话负责管理对数据库的连接以及执行各种数据库操作。 DbContext类作用如下: 管理数据库连接DbContext 负责管理与数据库的连接。它会在需要时打开连接在完成操作后关闭连接。跟踪实体对象DbContext 能够追踪实体对象的状态Added、Modified、Deleted 等。这使得在保存更改时EF 可以知道应该如何更新数据库。查询和操作数据库通过 DbContext 可以执行各种查询和操作例如增加、删除、更新数据。定义模型和关系在 DbContext 中你可以定义实体类之间的关系如一对一、一对多、多对多等。 有了上下文并且关联了实体之后你就可以通过上下文对象来操作数据表了。 上下文的生命周期始于实例创建时终于实例被处理Dispose或GC回收。如果你想在块的末尾释放上下文控制的所有资源请使用using。当你使用using时编译器会自动创建一个try/finally块并在finally块中调用dispose。 使用示例: 首先你需要定义一个继承自 DbContext 的类以及你要在数据库中映射的实体类。 假设我们有一个简单的博客应用有两个实体类Post 和 Author。 //在这个例子中我们定义了一个 BlogDbContext 类继承自 DbContext。 //它包含了两个 DbSet 属性分别对应数据库中 Posts 和 Authors 表。 public class BlogDbContext : DbContext {public DbSetPost Posts { get; set; }public DbSetAuthor Authors { get; set; } } 接下来我们可以使用 BlogDbContext 来进行数据库操作 //在这个示例中我们创建了一个新的 BlogDbContext 实例。然后我们创建了一个新的文章和一个新的作者并将文章与作者关联。最后通过 SaveChanges 方法将更改保存到数据库。 //这只是一个简单的示例DbContext 还提供了许多其他功能如复杂查询、事务管理等。 using (var context new BlogDbContext()) {// 添加一篇新文章var newPost new Post { Title Hello World, Content This is the first post. };context.Posts.Add(newPost);// 添加一个作者var author new Author { Name John Doe };context.Authors.Add(author);// 关联文章和作者newPost.Author author;// 保存更改到数据库context.SaveChanges(); }实体类型 上下文Context的派生类中包含一种实体类型的DbSet表示该实体存在于EF Core的模型中 internal class MyContext : DbContext {// Blog是实体public DbSetBlog Blogs { get; set; }... }通常称这样的类型为实体Entity所以实体就是类,是DbSet xxx中的T而不是DbSet xxx。 EF Core能从数据库中读数据至实体的实例或将实体实例写入数据库并且如果你用的是关系数据库EF Core能通过migrations为你的实体建表。 在model中添加实体类型 按照约定指EF Core技术设计使用上的规定在定义的上下文类中的DbSet属性中暴露的类型T会作为实体被包含在model中。在OnModelCreating方法中指定的实体类型也会被包含在内并且由递归探索到的实体类型的导航属性所找到的任何类型也会被包含在内。 在下面代码示例中包含了上面描述的意思; //Blog实体是被包含在model内的它在上下文中由DbSet属性暴露 //Post实体是被包含在model内它通过Blog.Posts导航属性可以发现 //AudiEntry是被包含在model内它在OnModelCreating方法中被指定。 internal class MyContext : DbContext {//DbSetpublic DbSetBlog Blogs { get; set; }//override方法protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.EntityAuditEntry();} }//entity public class Blog {public int BlogId { get; set; }public string Url { get; set; }public ListPost Posts { get; set; } }//entity public class Post {public int PostId { get; set; }public string Title { get; set; }public string Content { get; set; }public Blog Blog { get; set; } }//entity public class AuditEntry {public int AuditEntryId { get; set; }public string Username { get; set; }public string Action { get; set; } } 在model中排除实体类型 如果你不想某个类型被包含到模型中你可以排除它 // 两种方式 // 1. 数据标注方式 [NotMapped] public class BlogMetadata {public DateTime LoadedFromDatabase { get; set; } } // 2. Fluent API方式 protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.IgnoreBlogMetadata(); }配置表名 按照约定每个实体类型对应的表名将被设置为暴露实体的DbSet属性变量有着相同名称的数据表。若给定的实体不存在DbSet则使用类名。 同时也可以手动配置实体对应的表名 // 两种方式 // 1. 数据标注 [Table(blogs)] public class Blog {public int BlogId { get; set; }public string Url { get; set; } }// 2. Fluent API protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityBlog().ToTable(blogs); }Table schema 感觉中数据库的关系是这样的 数据服务器下面有数据库数据库下面有数据表表中有字段仅此而已。 实际上当打开一个数据服务器的连接后展现的树状图如下: 显然它们都是在一个叫SCHEMAS的根节点下的。所以这个图标是数据库形状的子节点居然是schema! 网上说法很多但是就目前来看在MySQL中把schema理解成database没有大问题。 当使用关系数据库时表按照约定会创建到你数据库中默认的schema中。 也可以按照下面方式为每个表指定其对应的schema: // 1. 数据标注 [Table(blogs, Schema blogging)] public class Blog {public int BlogId { get; set; }public string Url { get; set; } }// 2. Fluent API protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityBlog().ToTable(blogs, schema: blogging); } 除了为每个表指定schema你也可以使用fluent API在模型上定义默认的schema protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.HasDefaultSchema(blogging); }注意设置默认的schema还会影响其他的数据库对象例如序列sequence。 表备注 可以在数据表上设置任意文本注释 // 1. 数据标注 EF Core 5.0引入的 [Comment(Blogs managed on the website)] public class Blog {public int BlogId { get; set; }public string Url { get; set; } }// 2. fluent API protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityBlog().HasComment(Blogs managed on the website); } 若使用的是关系数据库实体属性将映射到数据表的列。 如何包含或排除属性 按照约定所有带有getter和setter的公开属性都将被包含在模型中。 可以通过以下方式排除指定的属性 // 1. 数据标注 public class Blog {public int BlogId { get; set; }public string Url { get; set; }[NotMapped]public DateTime LoadedFromDatabase { get; set; } }// 2. fluent API protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityBlog().Ignore(b b.LoadedFromDatabase); }自定义实体属性对应的列名 按照约定当使用关系数据库时实体的属性会映射到和属性有着相同名称的表的列上。当然也可以自定义列名: // 1. 数据标注 public class Blog {[Column(blog_id)]public int BlogId { get; set; }public string Url { get; set; } }// 2. fluent API protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityBlog().Property(b b.BlogId).HasColumnName(blog_id); } 自定义实体属性对应的列数据类型 // 1. 数据标注 public class Blog {public int BlogId { get; set; }[Column(TypeName varchar(200))]public string Url { get; set; }[Column(TypeName decimal(5, 2))]public decimal Rating { get; set; } }// 2. fluent API protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityBlog(eb {eb.Property(b b.Url).HasColumnType(varchar(200));eb.Property(b b.Rating).HasColumnType(decimal(5, 2));}); }配置必需的属性和可选的属性(按照约定法) 按照约定一个属性若它的.NET类型可以包含null则被配置为可选的反之则是必需/必要属性。 例如所有带有.NET值类型的属性intdecimalbool等等是必需属性而所有带有可为null的.NET值类型的属性int?decimal?bool?等等配置为可选属性。 // 1. 禁用NRT public class CustomerWithoutNullableReferenceTypes {public int Id { get; set; }[Required] // Data annotations needed to configure as requiredpublic string FirstName { get; set; }[Required]public string LastName { get; set; } // Data annotations needed to configure as requiredpublic string MiddleName { get; set; } // Optional by convention }// 2. 启用NRT public class Customer {public int Id { get; set; }public string FirstName { get; set; } // Required by conventionpublic string LastName { get; set; } // Required by conventionpublic string? MiddleName { get; set; } // Optional by convention// Note the following use of constructor binding, which avoids compiled warnings// for uninitialized non-nullable properties.public Customer(string firstName, string lastName, string? middleName null){FirstName firstName;LastName lastName;MiddleName middleName;} }配置必须的属性和可选的属性(显式配置法) // 1. 数据标注 public class Blog {public int BlogId { get; set; }[Required]public string Url { get; set; } }// 2. fluent API protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityBlog().Property(b b.Url).IsRequired(); }配置主键 按照约定一个属性命名为Id或 Id就会被配置成实体的主键 internal class Car {// Id被配置为主键public string Id { get; set; }public string Make { get; set; }public string Model { get; set; } }internal class Truck {// TruckId被配置为主键public string TruckId { get; set; }public string Make { get; set; }public string Model { get; set; } }也可以按以下方式将单个属性配置为主键 // 1. 数据标注 internal class Car {[Key]public string LicensePlate { get; set; }public string Make { get; set; }public string Model { get; set; } }// 2. fluent API protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityCar() // 模型中取出Car实体.HasKey(c c.LicensePlate); // 使LicensePlate成为主键 }也可以将多个属性配置为实体的主键——这被称为组合键有点超码的概念。组合键只能用fluent API来配置 // 1. fluent API配置组合键 protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.EntityCar().HasKey(c new { c.State, c.LicensePlate }); }总结: 实体是个类它是用来与数据库通信的它本身不能起通信作用需要以DbSet来在上下文中暴露才行 数据库迁移 当使用 Entity Framework (EF) 或 EF Core 时数据库迁移migration是一种用于管理数据库架构变化的工具。它允许你在应用程序的开发过程中轻松地对数据库模型进行更改而无需手动更新数据库结构。 保持数据库模式与应用程序模型同步随着应用程序的不断演进你可能需要修改实体类模型添加新实体、更改字段或关系等。数据库迁移帮助你将这些变化应用到数据库结构以便它与模型保持一致。版本控制数据库结构每个数据库迁移都是一个版本它记录了数据库模式的变化。这使你能够追踪数据库结构的历史和演化还可以轻松地回滚到先前的版本。简化数据库部署使用迁移你可以将数据库的模式变更作为代码提交到版本控制系统然后在部署新版本应用程序时通过运行迁移命令自动更新数据库。 使用示例: 首先确保你的.NET Core项目中已经安装了Entity FrameworkEF或EF Core的相关包如果没有可以使用以下命令安装 dotnet add package Microsoft.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.Design接下来你需要创建数据库上下文DbContext类以及实体类。 //假设有一个简单的博客应用包括文章Post和作者Author两实体。 //我们创建了一个 BlogDbContext 类继承自 DbContext并定义了两个 DbSet 属性分别对应数据库中的文章和作者。 public class BlogDbContext : DbContext {public DbSetPost Posts { get; set; }public DbSetAuthor Authors { get; set; } } 接下来为了启用数据库迁移你需要使用以下命令创建一个初始迁移 dotnet ef migrations add InitialMigration这将生成一个迁移文件其中包含了将模型映射到数据库结构所需代码。 然后运行迁移以将这些变更应用到数据库 dotnet ef database update如果你之后对模型进行更改可以再次创建并应用迁移以保持数据库结构的同步 dotnet ef migrations add NewChange dotnet ef database update命令会自动生成迁移文件描述了模型的新状态并将其应用到数据库。 总之数据库迁移是.NET Core中的EF或EF Core框架的一个强大功能用于管理数据库模式的变化使开发和维护数据库变得更加方便和可控。 创建并配置模型 EF/EFCore使用一系列约定来构建基于实体类特征的模型。 同时也可以指定额外的配置来补充或重写约定的内容。 用API配置模型 可以在派生的上下文Context类中重写OnModelCreating方法并使用ModelBuilder API来配置你的模型。 这是最有效的配置方法它允许你在不修改实体类的情况下指定配置。 注意: fluent API配置有着最高的优先级并会覆盖约定和数据标注。 using Microsoft.EntityFrameworkCore;namespace EFModeling.EntityProperties.FluentAPI.Required;//实体类 public class Blog {public int BlogId { get; set; }public string Url { get; set; } } //派生的上下文Context类 internal class MyContext : DbContext {//DbSetpublic DbSetBlog Blogs { get; set; }//重写: 派生的上下文类中的OnModelCreating方法#region Requiredprotected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.EntityBlog().Property(b b.Url).IsRequired();}#endregion }同时, 为了减小OnModelCreating方法的大小让里面代码少一点实体类型的所有配置项可以提取到一个实现了IEntityTypeConfigurationTEntity接口的单独的类中。 //如下所示: public class BlogEntityTypeConfiguration : IEntityTypeConfigurationBlog {public void Configure(EntityTypeBuilderBlog builder){builder.Property(b b.Url).IsRequired();} }然后在OnModelCreating方法中调用上面的Configure方法即可。 用数据标注配置模型 还可以应用特性也称为数据标注数据注释Data Annotations到 实体类和属性上。 注意: 数据标注会覆盖约定但会被Fluent API配置项给覆盖。 using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore;namespace EFModeling.EntityProperties.DataAnnotations.Annotations;//应用数据标注 或者说 特性到实体类上 [Table(Blogs)] public class Blog {public int BlogId { get; set; }///应用数据标注 或者说 特性到实体类的属性上[Required]public string Url { get; set; } }//派生的上下文Context类 internal class MyContext : DbContext {//DbSetpublic DbSetBlog Blogs { get; set; } }EF Core 数据库提供程序DataBase Provider数据库提供者其实就是数据库和你的应用程序之间的一个中间件供你访问数据库用的。 数据库提供程序将其转换为特定数据库的查询语言例如关系数据库的SQL。 对于EF Core使用的数据库提供程序的选择,直接在管理nuget中搜索: entityframeworkcore.xxx即可。 eg: 在nuget中搜索entityframework.mysql 区别 Entity Framework Core 是 Entity Framework 的轻量级可扩展和跨平台的版本. 兼容性EF是建立在传统的.NET框架上而EF Core则是建立在跨平台的.NET Core上。 所以EF仅适用于Windows平台而EF Core可以在多种平台包括Windows、Linux和macOS上使用。 功能EF拥有更丰富的功能集例如支持更多类型的数据库引擎、更多的查询操作和性能优化技术。而EF Core在一开始的版本中功能相对较少但随着版本的更新逐渐接近EF的功能水平。 性能由于EF Core是为了跨平台和性能优化而重新设计的它通常比EF的性能更好。然而具体的性能表现也取决于使用的数据库引擎、查询操作和应用程序的特定需求。 联系 核心概念EF和EF Core共享类似的核心概念如DbContext、实体类型Entity Type、集合导航属性Collection Navigation Property等。因此从EF迁移到EF Core时大部分代码可以保持不变。数据访问EF和EF Core都提供了CRUD创建、读取、更新、删除操作的API可以使用LINQ查询语句从数据库中检索和操作数据。数据库迁移EF和EF Core都支持数据库迁移工具可以根据模型的变化自动更新数据库架构。 总的来说EF适用于传统的.NET框架和Windows平台功能丰富而稳定而EF Core适用于跨平台的.NET Core性能较好但功能相对较少。在选择使用时需要考虑到应用程序的平台需求和性能要求。 EF Core连接Mysql 1: 安装 NuGet 包 首先你需要安装 Entity Framework Core 和 MySQL 数据库提供程序的 NuGet 包. //Pomelo.EntityFrameworkCore.MySql 是用于连接 MySQL 数据库的 Entity Framework Core 提供程序。 Pomelo.EntityFrameworkCore.MySql2: 配置数据库连接字符串 在 appsettings.json 文件中添加数据库连接字符串。例如 {ConnectionStrings: {MysqlConnection: serverlocalhost;port3306;user idroot;passwordSzcatic8616;} }将 Server、Database、User 和 Password 替换为你的 MySQL 数据库的实际信息。 3: 配置 DbContext 在你的应用程序中创建一个继承自 DbContext 的类该类将表示你的数据库上下文。 using Microsoft.EntityFrameworkCore;public class MyDbContext : DbContext {public MyDbContext(DbContextOptionsMyDbContext options): base(options){}// DbSet 属性用于表示数据库中的表//泛型T则表示实体 或者 表中的一条记录public DbSetT YourEntities { get; set; } }确保 YourEntity 类是你要在数据库中映射的实体类。 4: 注册 DbContext 在 Startup.cs 文件中的 ConfigureServices 方法中注册你的 DbContext。 public void ConfigureServices(IServiceCollection services) {// ...services.AddDbContextMyDbContext(options options.UseMySql(Configuration.GetConnectionString(MysqlConnection)));// ... }5: 创建数据库迁移 在终端中执行以下命令来创建数据库迁移 dotnet ef migrations add InitialMigration6: 应用数据库迁移 运行以下命令将迁移应用到数据库 dotnet ef database update7: 使用DbContext 现在可以在应用程序中使用 MyDbContext 来访问数据库了。例如 public class YourService {//继承的上下文Context类private readonly MyDbContext _context;//构造函数public YourService(MyDbContext context){_context context;}//利用context类对象进行数据访问和操作public ListYourEntity GetEntities(){return _context.YourEntities.ToList();} }以上就是在 ASP.NET Core 中使用 Entity Framework Core 连接 MySQL 数据库的基本步骤。如果你的实体模型有任何变化你可以通过运行 dotnet ef migrations add 和 dotnet ef database update 命令来更新数据库。 仓储模式 仓储模式Repository Pattern是一种常用的软件设计模式用于将数据访问逻辑与业务逻辑分离开来提高代码的可维护性和可测试性。 仓储模式的基本思想 仓储模式通过引入一个仓储Repository层将数据访问逻辑封装在该层中。这使得业务逻辑可以专注于业务处理而不必关心数据如何从数据库或其他持久化机制中获取。 仓储模式的元素 在.NET Core 中一个简单的仓储模式通常包括以下几个元素 实体类Entity表示数据模型通常对应数据库中的表格或其他持久化机制。仓储接口Repository Interface定义了对实体类进行增删改查的方法。具体仓储类Concrete Repository实现了仓储接口负责实际的数据访问工作。 仓储模式的分层 .NetCore简单仓储模型,共分为三层: 仓储层:Repository(类),IRepository(接口,接口添加泛型及约束) 业务层:Service(类,继承IService, 其构造函数引入IRepository) ​ IService(接口,接口添加泛型及约束) 控制器层:Controller(控制器层,其构造函数引入IService) 如下图所示: Repository继承IRepository,而IbaseRepository接口里面则写常用的增删改查方法。 注意事项: 一定要在Api的Program.cs配置文件里面添加各个层的注入,如下图: 示例: 假设我们有一个简单的 Product 类 public class Product {public int Id { get; set; }public string Name { get; set; }public decimal Price { get; set; } }仓储接口 public interface IProductRepository {Product GetById(int id);void Add(Product product);void Update(Product product);void Delete(int id); }具体仓储类(实现仓储接口的类) public class ProductRepository : IProductRepository {//【上下文context类】加readonly是编码习惯,防止被修改private readonly DbContext _context;//构造函数【传入上下文Context类】public ProductRepository(DbContext context){_context context;}public Product GetById(int id){return _context.Products.Find(id);}public void Add(Product product){_context.Products.Add(product);_context.SaveChanges();}public void Update(Product product){_context.Products.Update(product);_context.SaveChanges();}public void Delete(int id){var product _context.Products.Find(id);if (product ! null){_context.Products.Remove(product);_context.SaveChanges();}} }仓储模式的作用 解耦数据访问和业务逻辑通过使用仓储模式业务逻辑代码与具体的数据访问方式解耦提高了代码的可维护性和可测试性。方便切换数据访问方式如果以后需要更换数据访问方式例如从关系型数据库切换到 NoSQL 数据库只需要修改具体仓储类的实现而不需要修改业务逻辑。减少重复代码将数据访问逻辑封装在仓储中可以在多个业务逻辑中共享同一份数据访问代码避免重复实现相似的数据操作。 Identity框架 ASP.NET Core Identity 是一个用于处理用户认证和授权的框架它提供了一套通用的解决方案可以轻松地集成到 ASP.NET Core 应用程序中。它包括了用户管理、角色管理、登录、注册等功能。 Identity框架两个重要组件 Identity 框架提供了很多组件但最重要的莫过于两个 SignInManager 和 Identity 中间件。 SignInManager ( 登录管理器,验证用户 ) 顾名思义一旦密码验证通过SignInManager 就允许用户登录 当然了SignInManager 还可以用于登出一个用户 如果使用表单身份验证那么登录和注销通过管理 cookie 来实现。 当我们告诉 SignInManager 允许某个用户登录时 SignInManager 会向用户的浏览器返回一个 cookie浏览器接下来的每个后续请求中都会发送该 cookie直到 cookie 过期我们可以使用该 cookie 来识别用户 Identity 中间件(识别用户) Identity 中间件读取 SignInManager发送的 cookie 并识别用户一般情况下Identity 中间件都是在排在所有其它中间件之后才运行的 要使用该中间件需要将它配置到我们的应用程序管道中才能处理 SignInManager 设置的 Cookie。 Identity验证特性 验证用户身份的第一步就是要标识哪些控制器/哪些操作需要验证用户而这一步操作可以使用 Identity 框架提供的 [Authorize] 特性来解决。 可以把 [Authorize] 特性放在控制器上那么该控制器的所有方法都必须需要授权才能访问也可以放在控制器内的动作方法上那么只有该控制器的该动作方法需要授权访问。 默认情况下如果不传递其它参数给 Authorize授权检查的唯一项目就是确认用户是否已经登录当然也可传递参数来指定自定义授权策略。 [Authorize] public class HomeController : Controller { //.... [Authorize] public string index(){} } 除了 [Authorize] 外还有一个特性 [AllowAnonymous]。 当想要在使用了 [Authorize] 特性的某个控制器的动作方法上取消保护并允许匿名用户访问时可以使用该特性。 [Authorize] public ViewResult Index() {//....[AllowAnonymous] public string index(){} }注意: Authorize 与 AllowAnonymous 特性都在命名空间 Microsoft.AspNetCore.Authorization中所以在使用之前先要引入该命名空间。 ASP.NET Core请求过程 如图所示整个请求流程更加细化特别是对ASP.NET Core Application进行了放大其内部包含很重要的两个组件一个是Kestrel(作为托管的内部服务器,跨平台的)一个是管道管道包含多个中间件可以无限扩展。 ASP.NET Core Application ASP.NET Core Applicaton进一步放大可以了解到Kestrel其实在这里并没有做真正的核心处理只是做一层封装为HttpContext并往下传。真正处理请求的是管道管道其实就是RequestDelegate处理完成后封装成HttpContext进行回传当然HttpContext内含HttpRequest和HttpResponse。 Asp NetCore MVC Introduce MVC 体系结构模式将应用分成 3 组主要组件模型、视图和控制器。 此模式有助于实现关注点分离 UI 逻辑位于视图中,在 MVC 应用中视图仅显示信息。输入逻辑位于控制器中控制器处理用户输入和交互并对其进行响应。业务逻辑位于模型中。 控制器 在 ASP.NET MVC 应用程序中所有传入的请求均由控制器处理并将这些请求映射到控制器相应的方法上. 按照约定控制器的名称应该以 “Controller” 结尾例如 HomeController、ProductController 等。 MVC的简单控制器,如以下代码: using Microsoft.AspNetCore.Mvc; using System.Text.Encodings.Web;namespace MvcMovie.Controllers;public class HelloWorldController : Controller {// 指出这是一个 HTTP GET 方法它通过向基 URL 追加 /HelloWorld/ 进行调用// GET: /HelloWorld/public string Index(){return This is my default action...;}// 指定一个 HTTP GET 方法它通过向 URL 追加 /HelloWorld/Welcome/ 进行调用。// GET: /HelloWorld/Welcome/ public string Welcome(){return This is the Welcome action method...;} }控制器中的每个 public 方法均可被 HTTP 请求而调用。 对于控制器但更常见的做法是从 Microsoft.AspNetCore.Mvc 命名空间中提供的控制器基类中来派生控制器。 Microsoft.AspNetCore.Mvc 命名空间下的基类 Controller 让我们能够访问很多关于 HTTP 请求的上下文信息以及提供了一些方法帮助我们构建返回给回客户端的结果 返回的响应的结果通常被封装到实现 IActionResult 接口的对象中有大量的不同类型的结果实现了该接口. 响应结果可以返回 JSON也可以返回 XML或者 HTML 视图等。 动作基本上可以返回任意不同类型的动作结果。 它们都有一个共同的基类ActionResult 下表列出了不同种类的动作结果及其行为 动作名称( 类 )行为ContentResult返回一串字符串ObjectResult创建对象,返回对象。当返回一个 ObjectResult 时MVC 框架将访问这个对象,并将这个对象做一些转换然后作为 HTTP 响应返回给客户端。转换 ObjectResult 对象时它可能被序列化为 XML 或 JSON 或其它格式 至于什么格式由应用程序启动时向 MVC 提供的配置信息决定。如果没有显式的配置任何东西那么将使用 JSON 作为默认格式。 即ObjectResult类型是准许内容协商的。FileContentResult返回文件的内容FilePathResult返回路径文件的内容FileStreamResult返回流文件的内容EmptyResult返回空JavaScriptResult返回一段 JavaScript 代码JsonResult返回 JSON 格式的数据RedirectToResult重定向到某个 URLHttpUnauthorizedResult返回 403 未授权状态码RedirectToRouteResult重定向到不同的控制器或方法ViewResult从视图引擎中返回一个响应PartialViewResult从视图引擎中返回一个响应 demo using System; using Microsoft.AspNetCore.Mvc;namespace HelloWorld.Controllers {public class HomeController: Controller{public ContentResult Index() { return Content(你好世界! 这条消息来自使用了 Action Result 的 Home 控制器); } } }/* *我们可以看到Index() 方法返回了一个 ContentResult 类型的结果。ContentResult 是实现了 ActionResult 接口的不同结果类型之一在 Index() 方法中我们将一个字符串传递给 Content()。 Content() 方法会产生一个 ContentResult也就是说Index() 方法会返回 ContentResult */模型绑定 模型绑定即:自动将 HTTP 请求中的数据映射到操作方法参数。 这使得我们能够方便地从表单、URL参数或JSON等数据源中提取信息并将其转换为C#对象。 模型绑定的基本方式 从路由获取数据 如果你在路由中定义了参数比如 [Route(api/users/{id})] public IActionResult GetUser(int id) {// ... }那么id会自动从路由中提取出来。 从查询字符串获取数据 如果数据是作为查询字符串参数传递的例如 GET /api/users?id10那么可以直接在方法中添加一个参数 public IActionResult GetUser(int id) {// ... }模型绑定会自动将id从查询字符串中提取出来。 从请求体获取数据 当你发送一个POST请求时数据通常会包含在请求体中比如一个表单或一个JSON对象。你可以在方法参数中使用一个类来接收整个对象模型绑定会自动将请求体中的数据映射到这个类的属性上。 [HttpPost] public IActionResult CreateUser([FromBody] User user) {// ... }其中User是一个表示用户的类。 从路由值获取数据 有时数据可能是作为路由的一部分传递的但是它并不是直接作为参数提供的。在这种情况下你可以使用[FromRoute]属性来明确指定从路由中获取数据。 [HttpGet(api/users/{id})] public IActionResult GetUser([FromRoute] int id) {// ... }从表单获取数据 当你发送一个带有表单数据的POST请求时你可以在方法参数中使用一个类来接收整个对象。 [HttpPost] public IActionResult CreateUser([FromForm] User user) {// ... }从头部获取数据 你可以使用[FromHeader]属性来从HTTP头部中获取数据 [HttpGet] public IActionResult GetUser([FromHeader] string userAgent) {// ... }默认值 如果没有匹配到任何数据你可以提供一个默认值 public IActionResult GetUser(int id 1) {// ... }在没有传递id的情况下它会默认为1。 复合类型 你也可以在一个方法中同时使用多个以上的绑定方式比如同时从路由、查询字符串和请求体中获取数据 [HttpPost(api/users/{id})] public IActionResult UpdateUser(int id, [FromQuery] string role, [FromBody] User user) {// ... }以上是.NET Core中模型绑定的基本顺序和原理. 模型绑定顺序 模型验证 在ASP.NET Core中模型绑定后的数据可能需要进行验证以确保其符合预期的格式和规范。模型验证是一个重要的环节它可以帮助你确保接收到的数据是有效的。以下是ASP.NET Core中的模型验证 数据注解Data Annotations: 数据注解是一种常用的模型验证技术它通过在模型的属性上应用特性来定义验证规则。以下是一些常用的数据注解特性 [Required]: 指示该属性是必需的。[Range]: 指定属性的范围。[StringLength]: 指定字符串属性的最小和最大长度。[RegularExpression]: 指定属性的值必须符合指定的正则表达式。 例如 public class UserModel {[Required]public string UserName { get; set; }[Range(1, 100)]public int Age { get; set; }[EmailAddress]public string Email { get; set; } }模型状态验证: 控制器中的操作方法可以使用ModelState.IsValid属性来检查模型的状态。如果模型的数据符合规定ModelState.IsValid将返回true否则返回false。 [HttpPost] public IActionResult CreateUser(UserModel user) {if (ModelState.IsValid){// 数据验证成功可以进行后续操作return RedirectToAction(Success);}else{// 数据验证失败重新显示表单return View(user);} }在上面的例子中如果user模型的数据验证成功将会执行成功的逻辑否则将会返回包含验证错误信息的视图。 手动添加错误信息: 你也可以在控制器中手动添加错误信息到ModelState中 ModelState.AddModelError(FieldName, Error Message);例如 [HttpPost] public IActionResult CreateUser(UserModel user) {if (user.Age 18){ModelState.AddModelError(Age, Age must be at least 18.);}if (ModelState.IsValid){// 数据验证成功可以进行后续操作return RedirectToAction(Success);}else{// 数据验证失败重新显示表单return View(user);} }自定义验证: 你可以创建自定义的验证逻辑例如在控制器中实现一个自定义的验证方法并在需要的时候调用它。这可以用于处理一些特殊的业务逻辑验证。 private bool IsEmailUnique(string email) {// 自定义逻辑检查邮箱是否唯一 }[HttpPost] public IActionResult CreateUser(UserModel user) {if (!IsEmailUnique(user.Email)){ModelState.AddModelError(Email, Email is already in use.);}if (ModelState.IsValid){// 数据验证成功可以进行后续操作return RedirectToAction(Success);}else{// 数据验证失败重新显示表单return View(user);} }模型验证是保障接收到的数据合法性的重要环节。通过合理使用数据注解、模型状态验证和自定义验证可以有效地保证应用程序接收到的数据符合预期的格式和规范。 路由 需要找到一种方法将这些 HTTP 请求发送到正确的控制器。 在 ASP.NET Core MVC 中这个过程称为路由。 路由是指导 HTTP 请求到控制器的过程。 简单来说路由负责决定如何响应请求也就是将HTTP请求映射到应用程序中的的执行终结点例如一个方法或一个代码块。 MVC 中间件将根据我们提供的 URL 和一些配置信息做出此决定. 默认路由规则 在Startup.cs文件中的Startup类中Configure()方法中可以看到UseMvcWithDefaultRoute()中间件这是启动默认的MVC路由的功能。 这会给ASP.NET Core添加默认路由规则。 默认路由规则: {controllerHome}/{actionIndex}/{id?} 即如果没有指定控制器controller或动作action它将默认调用名为 Home 的控制器的 Index 动作。问号表示 id 参数可有可无。 //默认映射规则 // {controller}指定映射到相应的控制器,默认是HomeController // {action}指定映射到控制器的方法 ,默认是Index方法 // {id?}指定映射到控制器方法的参数称为模型绑定 故举例假设定义的路由模板为: {controllerHome}/{actionIndex}/{id}; 这意味者它会去寻找HomeController文件的Index方法,并向Index方法的形参传入具体的id值。 如果需要自定义映射规则可以使用UseEndpoints()中间件来设置端点然后在中间件当中设置如何映射。 //在StartUp.cs文件中的Configure方法 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {//去掉原先的app.UseMvcDefaultRoute()//app.UseMvcWithDefaultRoute();//使用下面的中间件来自定义路由映射规则【lambda表达式】app.UseEndpoints(endpoints {endpoints.MapControllerRoute(name: default,//路由名字//设置的路由模板【pattern中等号两边不能有空格】pattern: {controllerHome}/{actionIndex}/{id?});}); }也可以使用UseMvc()中间件来配置自定义映射规则: public void Configure(IApplicationBuilder app, IHostingEnvironment env) {//去掉原先的app.UseMvcDefaultRoute()//app.UseMvcWithDefaultRoute();//手动覆盖约定路由【template中等号两边不能有空格】app.UseMvc(routes {routes.MapRoute(name: default,template: {controllerHome}/{actionAbout}/{id?});});路由中间件 Routing路由更准确的应该叫做Endpoint Routing负责将HTTP请求按照匹配规则选择对应的终结点Endpoint终结点负责当HTTP请求到达时执行代码 路由是通过UseRouting和UseEndpoints两个中间件配合来完成注册的 UseRouting与UseEndpoints必须同时使用而且必须先调用UseRouting再调用UseEndpoints在调用UseRouting之前你可以注册一些用于修改路由操作的数据比如UseRewriter、UseHttpMethodOverride、UsePathBase等。在调用UseRouting和UseEndpoints之间可以注册一些用于提前处理路由结果的中间件如UseAuthentication、UseAuthorization、UseCors等。UseRouting()中间件 使用UseRouting()中间件启用路由功能。 然后使用UseEndpoints()中间件配置路由。 UseRouting注册了路由中间件负责根据请求信息匹配路由定义。 路由信息定义包含了路由的处理程序信息这些信息会在UseEndpoints方法中预先注册生成。 //1、启用默认路由 app.UseStaticFiles(); app.UseRouting(); app.UseMvcWithDefaultRoute();//2、自定义路由模板 app.UseStaticFiles(); app.UseRouting(); app.UseMvc(routes {routes.MapRoute(name: default,//注意下面的等号两边不能有空格template: {controllerHome}/{actionIndex}/{id?}); });//3、使用UseEndpoints自定义路由模板 app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints {endpoints.MapControllerRoute(name: default,//注意下面的等号两边不能有空格pattern: {controllerHome}/{actionIndex}/{id?});});约定路由 在 ASP.NET Core 中约定路由Convention-based Routing是一种基于约定的路由方式它使用一组命名约定来自动生成路由模板而无需显式地配置每个路由规则。 约定路由常用于创建一致性强的 RESTful API 或 Web应用程序路由。 (所有的路由定义都可以在同一个地方进行配置这些规则将被应用到所有的 Controller这也造成也其对一些特定的 URI 匹配不够灵活)。 需要定义一些包含一些参数化字符串的模板 例如/{controller}/{action}/{id},当接受到请求后会将请求的 URI 与这些模板进行匹配。 此时,MVC 根据入站 URL 中的具体值调用控制器类及其中的操作方法,以及如果涉及到传值,还会将值传递给方法作为方法实参。 特性路由 特性路由提供了一种直观的方式来定义路由规则,这种方式允许你在控制器或动作方法上直接使用特性来指定路由信息,而无需在全局配置文件设置。 提供更细粒度的控制。可以将路由信息直接与控制器或动作方法关联。无论你在特性路由中使用斜杠开头与否访问相同的 URL 时都会得到相同的结果。使用特性路由时还可以通过添加更多的属性来自定义路由的行为例如约束路由参数的类型、限制HTTP方法等。 使用特性路由的特性写法: [Route(这里书写路由Url字符串)] 限制Http请求方法的特性写法 [HttGet][HttpPost][HttpHead][HttpOptions][HttpDelete][HttpPacth][HttpPut] 这些特性中也可以填写字符串数据,其也是作为路由的组成部分。如: [HttGet(这里书写路由Url字符串)][HttpPost(这里书写路由Url字符串)]等等。 路由参数: 特性路由也支持路由参数可以通过使用大括号 {} 来定义参数 [Route(products/{id})] //其中 {id} 是占位符用于接收参数。 public IActionResult Details(int id) {// ... } //上述代码将 Details 方法映射到了 products/{id} 这个路由并且定义了一个名为 id 的路由参数其将传递给Details方法作为实参路由约束: 可以通过在路由参数后面添加约束来限制路由参数的类型 通过路由参数约束我们可以将路由模板中的参数限制为指定的类型通用的语法如下所示{parameter:constraint} //单个参数类型约束条件 //代码中的 :int 约束了 id 参数必须为整数类型。 [Route(api/product/{id:int})] public IHttpActionResult GetProduct(int id){}//多个参数类型约束条件,之间用:符号进行分割 [Route(api/product/{id:int:min(1)})] public IHttpActionResult GetProduct(int id){}下表罗列了可用的约束: 约束描述例子alpha将参数约束为大写或者小写的拉丁字母(a-z,A-Z){x:alpha}bool将参数限制为 bool 类型{x:bool}datetime将参数限制为 date{x:date}decimal将参数限制为 decimal 类型{x:decimal}float将参数限制为 32 位浮点数类型{x:float}double将参数限制为 64 位浮点数类型{x:double}int将参数限制为 32 整形{x:int}long将参数限制为 64位整形{x:long}guid将参数类型限制为 guid 类型{x:guid}length将参数的长度限制为指定长度或指定范围的长度{x:length(5)/{x:length(1,10)}min/max限制参数(整形)最大或最小值{x:min(10)}/{x:max(10)}minlength/maxlength限制参数的最小长度或最大长度{x:minlength(1)}/{x:maxlength}range限制参数(整形) 的范围包含两端{x:range(1,3)}regex限制参数必须匹配指定的正则表达式{x:regex(\expression)} 路由模板中的标记替换 特性路由支持标记替换标记替换是特性路由中的一种高级用法允许在路由模板中使用标记tokens这些标记将在运行时被替换为实际的值。这使得你可以根据实际情况动态地构建路由。 标记[action],[area],[controller]会被替换成操作所对应的操作名(方法名)区域名控制器名。 [controller]这个标记会被替换为控制器名字去掉Controller字符串后的名称。 例如如果你有一个名为ProductsController的控制器那么[controller]将在运行时替换为Products。 [action]这个标记会被替换为动作方法的名称。 例如如果你有一个名为GetProduct的动作方法那么[action]将在运行时被替换为GetProduct [area]: 这个标记会被替换为控制器所属的区域名称。 区域允许你将应用程序分成更小、更可管理的功能模块每个模块可以拥有自己的控制器、视图和静态资源等。 例如,如果你的控制器类所属区域为admin,那么[area]将在运行时被替换为admin. //例子 [Route([controller]/[action])]public class BlogController : Controller{[HttpGet]//匹配 Blog/GetAllpublic ActionResult GetAll(){return View();} }以下是使用特性路由的一些常见示例 控制器级别的特性路由 你可以在控制器类上使用 [Route] 特性来指定控制器的路由 //将 ProductsController 映射到了 api/products 这个路由。 [Route(api/products)] public class ProductsController : Controller {// ... }控制器方法级别的路由模式 你也可以在控制器的动作方法上使用 [Route] 特性来指定动作方法的路由 //代码将 List 方法映射到了 list 这个路由该路由将会是相对于控制器级别的路由的一部分。 public class ProductsController : Controller {[Route(list)]public IActionResult List(){// ...} }多个路由 可以具有多个路由这在特定情况下可能会很有用 [Route(products/{id:int})] [Route(items/{id:int})] public IActionResult Details(int id) {// ... } //上述代码中Details 方法可以通过 products/{id} 或者 items/{id} 两个路由访问。约定路由 和 特性路由 对比 特性路由修饰在Controller和Action上特性路由有更多灵活性。特性路由一般用于设置RESTful API控制器的映射。约定路由和特性路由可以混用。 路由的执行顺序 常规路由会根据定义顺序来执行与之相比特性路由会构建一个树形结构同时匹配所有路由。这种看起来像路由条目被放置在一个理想的顺序中最具体的路由会在一般的路由之前执行。比如路由blog/Edit/4 比 blog/{*article} 更加具体。 特性路由使用所有框架提供的路由特有的Order属性来配置顺序并根据Order属性升序处理路由。默认是0设置为-1时会在没有设置的路由之前执行。 视图 页面就是一个服务器端返回的数据但这个数据并不是直接存在的而是由控制器、模型、视图三大部分相互作用的结果。 默认情况下ASP.NET Core MVC 项目中视图以 .cshtml 作为扩展名 视图文件夹的对应关系 在 ASP.NET Core 中控制器名称和视图文件夹的命名对应关系是由约定来确定的。 这种约定遵循了一套命名规则以便 ASP.NET Core 可以自动找到与控制器相关联的视图文件。 按照约定控制器的名称应该以 “Controller” 结尾例如 HomeController、ProductController 等。 对应的视图文件夹应该位于 Views 文件夹下文件夹的名称应该与控制器的名称相匹配但不包括 “Controller” 后缀。 举个例子 如果有一个名为 HomeController 的控制器那么它对应的视图文件夹应该是 Views/Home且如果HomeController控制器里面有名为Index和About的方法,则对应的视图目录结构如下 /Views/HomeIndex.cshtmlAbout.cshtml这样当你在 HomeController 中返回一个视图时ASP.NET Core 将会自动查找 Views/Home 文件夹下的视图文件,并根据HomeController里的方法名去查找 Views/Home 文件夹下同名的视图文件。 需要注意的是你也可以在控制器的方法中通过参数指定视图的名称以覆盖默认的约定。 举个例子: public IActionResult SomeAction() {//这里的View()方法是属于父类Controller中的return View(CustomView); // 通过参数指定,从而使用名为 CustomView.cshtml 的视图从而覆盖默认的约定。 } 视图查找搜索顺序及过程: 【假设同时存在Pages文件夹和Views文件夹】 先查找Pages视图文件夹 首先ASP.NET Core 将会在 Pages 文件夹中按照请求路径查找对应的 Razor Page 文件。比如对于请求 /Home/Index它会在 Pages 文件夹下查找以下路径的 Razor Page视图 /Pages/Home/Index.cshtml 如果找到对应的 Razor Page 文件则会使用它作为视图。 如果没有找到与请求路径匹配的 Razor PageASP.NET Core 将会在 Pages 文件夹中查找一个名为 _ViewStart.cshtml 的文件作为 fallback 页面。/Pages/_ViewStart.cshtml _ViewStart.cshtml 文件可以设置一些全局的视图配置但通常情况下并不直接用作视图。 再查找Views视图文件夹 如果在控制器的方法中明确指定了视图的名称ASP.NET Core 将直接搜索该名称对应的视图文件。 return View(CustomView); // 将直接在Views文件夹中搜索 CustomView.cshtml如果没有指定视图名称ASP.NET Core 将按照约定搜索默认的视图文件。默认情况下其将直接查找与当前操作方法同名的视图文件。 public IActionResult SomeAction() {// 在 SomeAction 方法中return View(); // 将搜索 SomeAction.cshtml }如果没有直接找到与当前操作方法同名的视图文件ASP.NET Core 将搜索与控制器名称相同的文件夹中的与操作方法同名视图。 // 如果当前控制器名称为 HomeController且在 SomeAction 方法中 return View(); // 将搜索 Views/Home/SomeAction.cshtml如果以上步骤都没有找到对应的视图ASP.NET Core 将在 Views/Shared 文件夹中搜索视图文件。 // 如果没有找到 Views/Home/SomeAction.cshtml将搜索 Views/Shared/SomeAction.cshtml总的来说ASP.NET Core 通过这套搜索顺序来自动关联控制器和视图使得开发者可以更加便捷地管理视图文件。如果需要特殊处理可以在方法中通过参数指定视图的名称以覆盖默认的约定。 自定义视图路径 在 ASP.NET Core 中可以通过 View() 方法来指定返回的视图的路径和名称从而实现自定义视图路径。 View() 方法有多个重载可以根据需要传递不同的参数来实现不同的自定义视图路径。 指定完整的视图路径(绝对路径) **需以 / 开头的绝对路径 **其中/代表的是应用程序的根目录 需指定视图文件的完整路径包括视图文件名和扩展名。 两种路径写法: 以波浪线~开头的视图路径 或 以/开头的视图路径通常情况下建议使用带有波浪线的路径以确保路径始终相对于应用程序的根目录。 1.以~开头的视图路径 以~开头的路径可以确保你的路径始终相对于应用程序的根目录不受当前请求路径的影响。 波浪线路径是一种相对于应用程序根目录的路径表示方式可以确保路径的准确性和可靠性。 return View(~/Views/CustomViews/MyController/MyView.cshtml);2.以/开头的视图路径 这种写法和使用波浪线路径 ~ 类似也是相对于应用程序的根目录来引用文件。区别在于不带波浪线的相对路径可能会受到当前请求路径的影响而波浪线路径始终相对于应用程序的根目录。 return View(/Views/CustomViews/MyView.cshtml);指定相对于当前控制器或视图所在的文件夹而言的视图路径(相对路径) 不以/开头 需指定视图文件的相对路径仅需要视图文件名,不需扩展名。 return View(../../Views/CustomViews/MyView);使用约定的视图文件夹和视图名称 如果按照约定命名和组织视图文件可以省略掉路径的指定 return View(); // 会根据约定搜索视图从控制器传递数据到视图 在 ASP.NET Core 中控制器可以通过两种方式将数据传递给视图 弱类型和强类型。 弱类型传递数据到视图 ViewData ViewData 是一个字典可以在控制器和视图之间传递数据。它使用字符串键和对象值来存储数据。在控制器中设置数据然后在视图中使用相应的键来获取数据。 示例 // 在控制器中设置数据 ViewData[Message] Hello from ViewData;// 在视图中获取数据 h1ViewData[Message]/h1ViewBag ViewBag 是一个动态属性可以在控制器和视图之间传递数据。它允许你像使用属性一样设置和获取数据。可以减少一些键的书写但是需要注意类型转换。 示例 // 在控制器中设置数据 ViewBag.Message Hello from ViewBag;// 在视图中获取数据 h1ViewBag.Message/h1强类型传递数据到视图 使用模型传递数据 定义一个模型类将需要传递的数据封装在模型中。在控制器中将模型实例传递给视图。 示例 // 模型类 public class MyViewModel {public string Message { get; set; } }// 在控制器中传递模型实例 public IActionResult Index() {MyViewModel model new MyViewModel{Message Hello from Model};return View(model); }// 在视图中使用模型 model MyViewModelh1Model.Message/h1使用ViewModel传递数据 这里的ViewModel可以理解为Java中的DTO对象, 主要就是为了提供给前端或者视图使用的数据。 示例: 在项目中创建一个用于传递数据的 ViewModel 类。这个类通常包含了你想要传递给视图的属性。 public class MyViewModel {public string Message { get; set; } }在控制器的动作方法中实例化 ViewModel 并将数据赋值给它然后将 ViewModel 传递给视图。 public IActionResult Index() {MyViewModel model new MyViewModel{Message Hello from ViewModel};return View(model); }在视图中声明视图的模型类型然后就可以通过 Model 属性来访问 ViewModel 中的属性。 model MyViewModelh1Model.Message/h1通过使用 ViewModel我们可以将需要传递给视图的数据整合在一个类中并且可以利用类的属性来提供更加具体和类型安全的数据。这种做法使得代码更加清晰、易于维护并且提升了可读性。 总结来说弱类型传递数据使用 ViewData 和 ViewBag而强类型传递数据使用模型类。强类型传递数据更加类型安全能够提供更好的编译时检查和 IntelliSense 支持。 依赖注入 基本概念 服务Service 在 ASP.NET Core 中服务是指应用程序中的一个组件它执行特定的功能或提供特定的服务。 例如数据库连接、日志记录、配置管理等都可以是服务。 服务容器Service Container 服务容器是 ASP.NET Core 框架提供的一个内置容器它负责管理应用程序中所有的服务和它们的生命周期。 依赖Dependency 当一个组件如类、方法等需要使用另一个组件时它依赖于后者。依赖通常以构造函数参数、方法参数等形式注入。 依赖注入Dependency Injection 依赖注入是指将一个组件所依赖的其他组件服务通过构造函数、方法参数等方式传递给它而不是由它自己去创建或管理依赖。 依赖注入解决了这些棘手的问题 通过接口或基类包含抽象方法将依赖关系进行抽象化将依赖关系存放到服务容器中由框架负责创建和释放依赖关系的实例并将实例注入到构造函数、属性或方法中 什么是依赖注入? 工厂模式的思想知道这样做的好处提及到依赖注入通常会关联出两个概念Ioc(控制反转)和DI(依赖注入) 控制反转Inversion of Control缩写为IoC是面向对象编程中的一种设计原则可以用来减低计算机代码之间的耦合度。 其中最常见的方式叫做依赖注入Dependency Injection简称DI。 IoC(控制反转): 就是将原先的new对象这个操作交由第三方容器由容器统一创建对象并管理其创建对象的生命周期 DI(依赖注入): 其中“依赖”有两层意思 类与类之间的依赖关系 对象的创建依赖于容器 “注入”不用主动从容器中获取对象由容器根据对象依赖关系自动注入【注入的前提是要把对象的控制权交给容器】 依赖注入程序将对象控制权交给容器统一依赖容器创建对象类之间的依赖也是通过容器自动注入 ASP.NET Core 中的依赖注入DI是一个重要的设计模式它允许你在应用程序中组织和管理组件之间的依赖关系从而使代码更加模块化、可测试和可维护。 通俗地说依赖注入就像是把一些工具放在一个工具箱里当你需要用到某个工具时只需要向工具箱里拿即可不用自己去买买买也不用自己去管理这些工具的生命周期。 这就好像你在做菜的时候需要用到炒菜的锅、刀、砧板等工具。你不需要自己去买这些工具而是可以向厨房管理员ASP.NET Core提出你需要这些工具然后他会把它们交给你。你用完后也不需要自己清洗和管理你只需要把它们还给管理员即可。 总的来说ASP.NET Core 的依赖注入机制让我们可以在需要的地方“要求”所需的服务而不用自己去创建或者管理其生命周期。 依赖注入中的关键类型 NetCore中依赖注入有几个关键的类型简单介绍一下 IServiceCollection负责存储注册的服务可以通过其扩展方法进行服务注册ServiceDescriptor服务注册时的信息如服务类型、实现类型、实例类型、生命周期等IServiceProvider 理解是常说的容器是IServiceCollection创建出来的用来提供实例的IServiceScope表示一个容器的子容器的生命周期 基本使用步骤 1.注册服务 在程序启动时我们告诉 ASP.NET Core 我们需要哪些服务这个过程叫做注册服务。(即是一个往工具箱里放入工具的过程) 在 Startup.cs 文件的 ConfigureServices 方法中通过调用 services.AddXXX 方法来注册服务。例如 // 例如我们注册一个名为 IMyService 的服务并告诉 ASP.NET Core 它的具体实现类 MyService。 //这里IMyService 是接口MyService 是实现了该接口的具体类。 //注services 是指 IServiceCollection services services.AddScopedIMyService, MyService();有三种注册服务的方式 : 在 ASP.NET Core 中services.AddSingleton(), services.AddTransient(), 和 services.AddScoped() 是用于将服务注册到依赖注入容器的方法分别表示单例模式、瞬时模式和作用域模式 services.AddSingleton() services.AddSingletonTService, TImplementation() 将一个服务注册为单例模式也就是在整个应用程序的生命周期中只会创建一个实例之后的每次请求都会返回同一个实例其会在应用程序启动时创建并在应用程序关闭时销毁;单例服务是线程安全的可以在整个应用程序中共享状态适用于无状态的服务、共享配置信息等。 services.AddTransient() services.AddTransientTService, TImplementation() 将一个服务注册为瞬时模式也就是每次请求都会创建一个新的实例瞬时服务在每次请求时都会创建新的实例适用于无状态的、临时的操作;每次请求都会得到一个全新的实例不存在线程安全问题。 services.AddScoped() services.AddScopedTService, TImplementation() 将一个服务注册为作用域模式也就是在每一个 HTTP 请求处理过程中都会创建一个新的实例而在同一个http请求中多次获取该服务会得到相同的实例作用域服务在同一个 HTTP 请求处理过程中会保持相同的实例适用于需要在同一请求中共享状态的场景比如 DbContext。 范围或称为作用域即在某个范围或作用域内内获取的始终是同一个服务实例而不同范围或作用域间获取的是不同的服务实例。对于Web应用每个请求为一个范围或作用域。 2.接收使用服务(依赖注入) 当我们需要用到这个服务时ASP.NET Core 会自动帮我们把它提供出来我们只需要在需要的地方使用它。 例如 public class HomeController : Controller {private readonly IMyService _myService;// 构造函数中接收 IMyServiceASP.NET Core 会自动帮我们传递进来。//所谓“ASP.NET Core 会帮我们把它提供出来”指的是在我们创建 HomeController 实例时ASP.NET Core 会自动帮我们把 IMyService 这个服务的一个实例也就是 MyService 的一个实例传递给 HomeController 的构造函数。//这里的 IMyService myService 参数会在创建 HomeController 实例时自动注入。public HomeController(IMyService myService){_myService myService;}// 在需要的地方使用 _myService 即可。... }在需使用服务的地方将服务通过构造函数、方法参数等方式注入。 构造函数注入: public class HomeController : Controller {//在构造函数中将依赖项赋值给私有字段时将字段标记为 readonly 通常是一个良好的实践。readonly 关键字确保了一旦在构造函数中给字段赋值就不能再次在类的其他地方更改它的值。//总的来说尽管在这种情况下 readonly 不是必需的但它是一种良好的实践可以帮助你保持代码的一致性和可维护性。private readonly IMyService _myService;public HomeController(IMyService myService){_myService myService;}// ... }方法参数注入 //这种方式通常用于在控制器的具体方法中获取所需的服务。 //[FromServices] 是 ASP.NET Core 中用于在控制器的动作方法中获取服务。它告诉 ASP.NET Core 从依赖注入容器中获取指定类型的服务并将其传递给动作方法。//例如如果你在一个控制器的动作方法中需要使用某个服务可以将该服务的类型作为参数并在参数前加上 [FromServices] 属性 //这样ASP.NET Core 将会自动从依赖注入容器中找到 IMyService 的实现并传递给 DoSomeing 方法。//对于方法参数的依赖注入来说[FromServices] 是一个可选的特性。如果你在方法参数中使用了 [FromServices]ASP.NET Core 将会尝试从依赖注入容器中获取相应的服务并传递给该参数。如果没有使用 [FromServices]你也可以在方法内部使用 HttpContext.RequestServices.GetServiceTService() 来手动获取所需的服务。 public IActionResult DoSomeing([FromServices] IMyService myService) {// ... }直接从 HttpContext.RequestServices 中获取 如果在非控制器类中需要获取服务你可以使用 ()HttpContext.RequestServices.GetService(typeof(xx)) //这种方式可以在任何地方通过 HttpContext.RequestServices 来获取所需的服务但通常不推荐在控制器或服务之外的地方使用。 public class HomeController : Controller { public IActionResult SomeAction(){var myService (IMyService)HttpContext.RequestServices.GetService(typeof(IMyService));// ...} } 这三种方式可以根据实际情况选择使用通常情况下构造函数注入是最常用且推荐的方式因为它可以保证服务在类被创建时就可用并且能够提高代码的可测试性和可维护性。其他方式则在特定场景下可能会更为方便。 Replace Remove 扩展方法 有时我们想要替换或是移除某些服务这时就需要使用Replace和Remove了。 // 将 IMyService 的实现替换为 MyService1 services.Replace(ServiceDescriptor.SingletonIMyService, MyService()); // 移除 IMyService 注册的实现 MyService services.Remove(ServiceDescriptor.SingletonIMyService, MyService()); // 移除 IMyService 的所有注册 services.RemoveAllIMyService(); // 清除所有服务注册Autofac Autofac 是一个老牌DI组件可使用Autofac替换ASP.NET Core自带的DI容器。 安装nuget包 Install-Package Autofac Install-Package Autofac.Extensions.DependencyInjection替换服务提供器工厂 public static IHostBuilder CreateHostBuilder(string[] args) Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder {webBuilder.UseStartupStartup();})// 通过此处将默认服务提供器工厂替换为 autofac.UseServiceProviderFactory(new AutofacServiceProviderFactory());具体可参考学习链接: 跟我一起学.NetCore之Asp.NetCore中集成Autofac扩展 (qq.com) 更多内容 可参考学习链接: #跟我一起学.NetCore (qq.com) 最后附上一张.Net修仙图(学习路线) The End!!创作不易,欢迎点赞/评论!!欢迎关注个人GZH!!
http://www.pierceye.com/news/149841/

相关文章:

  • 网站建设方案书怎么写安徽和住房建设厅网站
  • 北京市住房和城乡建设厅官方网站重庆百度seo整站优化
  • 备案ip 查询网站查询网站河南建筑职业技术学院
  • 均安公司网站建设免费建手机个人网站
  • 南京做网站的网络公司排名wordpress发邮件更新
  • 抽奖的网站怎么做美食类网站模板
  • 自己建一个网站难吗网络安全行业公司排名
  • 做招聘的h5用哪个网站企业网站需要多大空间
  • 织梦 公司网站模板html5网站开发的源码
  • 晋江网站建设公司电脑培训网
  • 电子商务网站开发的题网站关键词排名怎么提升
  • 在百度网站备案查询上显示未备案是什么意思wordpress资源分享主题
  • 夏县做网站郑州做商城网站
  • 网站首页推荐网络服务提供者发现用户利用其网络服务对未成年
  • 中外网站建设区别微信软文是什么意思
  • 苏州网站建设极简幕枫卫浴网站建设
  • 优秀企业网站欣赏网站的备案怎么处理
  • 怎样做古玩网站毕业设计开题报告网站开发
  • 西安网站 建设app注册推广
  • 丹徒网站建设公司代理公司注册价格
  • 网站建站建设网站中国商标商标查询网
  • 机械加工网站平台南京app制作开发公司
  • 用vs2008做网站教程seo推广网址
  • 正规制作网站公司哪家好视觉传达设计专业作品集
  • 做网站多少钱特惠西宁君博s网站网站建设多少钱
  • 建筑模版东莞网站建设技术支持手机网站开发学习
  • 专业网站建设效果显著做设计找参考的设计网站有那些
  • 最新网站建设技术2022年新闻摘抄简短
  • 手机网站总是自动跳转最吃香的男生十大手艺
  • 免费网站推广软件哪个好企业vi设计公司价格