关于asp.net-core:IOC容器技术ServiceCollection

前言什么是依赖注入?依赖注入(Dependency Injection),简称DI。 因为某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点。在程序运行过程中,客户类不间接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,而后将其注入到客户类中,保障客户类的失常运行。依赖关系反转应用程序中的依赖关系方向应该是形象的方向,而不是实现详细信息的方向。 大部分应用程序都是这样编写的:编译时依赖关系顺着运行时执行的方向流动,从而生成一个间接依赖项关系图。 也就是说,如果类 A 调用类 B 的办法,类 B 调用 C 类的办法,则在编译时,类 A 将取决于类 B,而 B 类又取决于类 C,如图所示 间接关系依赖利用依赖关系反转准则后,A 能够调用 B 实现的形象上的办法,让 A 能够在运行时调用 B,而 B 又在编译时依赖于 A 管制的接口(因而,典型的编译时依赖项产生反转)。 运行时,程序执行的流程放弃不变,但接口引入意味着能够轻松插入这些接口的不同实现。 反转依赖项关系图依赖项反转是生成涣散耦合应用程序的要害一环,因为能够将实现详细信息编写为依赖并实现更高级别的形象,而不是相同。 因而,生成的应用程序的可测试性、模块化水平以及可维护性更高。 遵循依赖关系反转准则可实现依赖关系注入。NET 中的依赖关系注入.NET 反对依赖关系注入 (DI) 软件设计模式,这是一种在类及其依赖项之间实现管制反转 (IoC) 的技术。 .NET 中的依赖关系注入是框架的内置局部。内置的IOC容器通过nuget增加援用 Microsoft.Extensions.DependencyInjection Microsoft.Extensions.DependencyInjection.Abstractionsinternal class Program{ private static void Main(string[] args) { DbHelper dbHelper = new DbHelper(); dbHelper.DoConnect(); }}public class DbHelper{ private readonly Redis redis = new Redis(); public void DoConnect() { redis.Connect(); }}public class Redis{ public void Connect() { Console.WriteLine($"HashCode:{this.GetHashCode()}Redis.....Connect"); }}在下面代码中,类DbHelper创立并间接依赖于Redis类。 硬编码的依赖项会产生问题,应防止应用,起因如下: ...

August 29, 2022 · 2 min · jiezi

关于asp.net-core:NET-7-预览版2-中的-ASPNET-Core-更新

.NET 7 预览版2 现已推出,其中包含对ASP.NET Core 的许多重大改良。 以下是此预览版中新增内容的摘要: 推断来自服务的API 控制器操作参数SignalR 集线器办法的依赖注入为minimal API 提供端点形容和摘要在最小的API 中绑定来自标头和查问字符串的数组和StringValue自定义cookie 批准值无关为.NET 7 打算的ASP.NET Core 工作的更多详细信息,请参阅GitHub 上的.NET 7 的残缺ASP.NET Core 路线图。 开始应用要开始应用.NET 7 Preview 2 中的ASP.NET Core,请装置.NET 7 SDK。 如果您在Windows 上应用Visual Studio,咱们倡议装置最新的Visual Studio 2022 预览版。Visual Studio for Mac 对.NET 7 预览的反对尚不可用,但行将推出。 要装置最新的.NET WebAssembly 构建工具,请从晋升的命令提示符处运行以下命令: dotnet workload install wasm-tools 降级现有我的项目要将现有的ASP.NET Core 利用从.NET 7 Preview 1 降级到.NET 7 Preview 2: 将所有Microsoft.AspNetCore. 包援用更新到7.0.0-preview.2.。将所有Microsoft.Extensions. 包援用更新到7.0.0-preview.2.。另请参阅.NET 7 的ASP.NET Core 中的重大更改的残缺列表。 推断来自服务的API 控制器操作参数当类型配置为服务时,API 控制器操作的参数绑定当初通过依赖注入绑定参数。 这意味着不再须要将[FromServices] 属性显式利用于参数。 ...

April 15, 2022 · 2 min · jiezi

关于asp.net-core:ASPNET-Core-6-的性能改进

受到 由Stephen Toub 公布的对于 .NET 性能的博客的启发,咱们正在写一篇相似的文章来强调ASP.NET Core 在6.0 中所做的性能改良。 基准设置咱们整个过程中大部分的实例应用的是BenchmarkDotNet。在https://github.com/BrennanConroy/BlogPost60Bench上提供了repo,其中包含本文中应用的大多数基准。 本文中的大多数基准测试后果都是通过以下命令行生成的: dotnet run -c Release -f net48 --runtimes net48 netcoreapp3.1 net5.0 net6.0 而后从列表中抉择要运行的特定基准。 这命令行给BenchmarkDotNet指令: 在公布配置中构建所有内容。针对 .NET Framework 4.8 外围区域构建它。在 .NET Framework 4.8、.NET Core 3.1、.NET 5 和 .NET 6 上运行每个基准测试。对于某些基准测试,它们仅在 .NET 6 上运行(例如,如果比拟同一版本上的编码的两种形式):dotnet run -c Release-f net6.0--runtimes net6.0对于其他人,只运行了版本的一个子集,例如dotnet run -c Release-f net5.0--runtimes net5.0 net6.0我将包含用于运行每个基准测试的命令。本文中的大多数后果都是在Windows上运行上述基准测试生成的,次要是为了将. NET Framework 4.8蕴含在后果集中。 然而,除非另有阐明,一般来说,所有这些基准测试在Linux或macOS上运行时都显示出相当显著的改良。只需确保您曾经装置了想要测量的每个运行时。这些基准测试应用的是.NET 6 RC1的构建,以及最新公布的.NET 5和.NET Core 3.1下载。 span< T > 自从在.NET 2.1中减少了Span< T >,之后的每一个版本咱们都转换了更多的代码以在外部和作为公共API的一部分应用Span来进步性能。这次公布也不例外。 ...

April 8, 2022 · 4 min · jiezi

关于asp.net-core:ASPNET-Core-6-的性能改进

受到 由Stephen Toub 公布的对于 .NET 性能的博客的启发,咱们正在写一篇相似的文章来强调ASP.NET Core 在6.0 中所做的性能改良。 基准设置咱们将在整个过程中大部分的实例应用BenchmarkDotNet。在https://github.com/BrennanCon...上提供了一个回购协定,其中包含本文中应用的大多数基准。 本文中的大多数基准测试后果都是通过以下命令行生成的: dotnet run -c Release -f net48 --runtimes net48 netcoreapp3.1 net5.0 net6.0而后从列表中抉择要运行的特定基准。这命令行给BenchmarkDotNet指令:• 在公布配置中构建所有内容。• 针对 .NET Framework 4.8 外围区域构建它。• 在 .NET Framework 4.8、.NET Core 3.1、.NET 5 和 .NET 6 上运行每个基准测试。• 对于某些基准测试,它们仅在 .NET 6 上运行(例如,如果比拟同一版本上的编码的两种形式): dotnet run -c Release -f net6.0 --runtimes net6.0对于其他人,只运行了版本的一个子集,例如 dotnet run -c Release -f net5.0 --runtimes net5.0 net6.0我将包含用于运行每个基准测试的命令。 本文中的大多数后果都是在Windows上运行上述基准测试生成的,次要是为了将. NET Framework 4.8蕴含在后果集中。 然而,除非另有阐明,一般来说,所有这些基准测试在Linux或macOS上运行时都显示出相当显著的改良。只需确保您曾经装置了想要测量的每个运行时。这些基准测试应用的是.NET 6 RC1的构建,以及最新公布的.NET 5和.NET Core 3.1下载。 ...

March 5, 2022 · 4 min · jiezi

关于asp.net-core:aspnet-core的api版本管理最佳实践

什么要须要api版本控制一开始,零碎有一个api为客户A服务,随着业务倒退,零碎进行了降级,这个api也须要调整。然而,我还须要它持续能反对客户A,所以须要引入版本来治理。多个版本的API,相互之间的行为是不雷同的。 在asp.net core中实现api 版本形式有哪些?基于申请参数基于URL基于申请头安装包微软提供了独立的包反对,通过命令行或者nuget包管理工具引入 Install-Package Microsoft.AspNetCore.Mvc.Versioning在启动类中启用api版本定义扩大办法,启用配置 public static class ApiVersioningExtensions { public static IServiceCollection AddApiVersioning(this IServiceCollection services) { services.AddApiVersioning(config => { // 指定默认版本 config.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(1, 0); // 如果客户端没有在申请中指定api版本,则应用默认版本 config.AssumeDefaultVersionWhenUnspecified = true; // 颁布api版本 config.ReportApiVersions = true; }); return services; } }最佳目录构造将不同版本的api放到不同的目录下,职责更加清晰 基于url的api版本如何实现在url中明确晓得申请版本,我集体比拟喜爱这种 https://xxxx/api/v1/user https://xxxx/api/v2/user 1.0的api定义 namespace ApiVersioning.Controllers.V1{ [Route("api/v{version:apiVersion}/[controller]")] [ApiController] [ApiVersion("1.0")] public class UserController : ControllerBase { [HttpGet] public string Get() { return "use from api v1"; } }}2.0的api定义 ...

August 5, 2021 · 1 min · jiezi

关于asp.net-core:云原生ASPNET-Core程序的可监测性和可观察性

分布式应用程序很简单,给开发人员调试和修复生产问题带来了一系列挑战。只管微服务架构可帮忙维持一支规模较小,能够自主工作并专一于独立业务团队,但因为其分布式性质,它带来了新的挑战。例如,在业务交易过程中呈现问题的状况下,须要端到端跟踪申请,该申请可能逾越多个服务和基础架构。 解决问题时可能遇到的挑战有: 治理已知和未知故障故障也是分布式的传统监控零碎不实用这是可监测性和可察看性呈现的中央。可监测性记录应用程序的总体运行状况,而可察看性则能够帮忙您更深刻地理解上下文数据。在.NET大会上,我和Cecil 曾经深刻探讨了云原生应用程序中的可监测性和可察看性。 https://www.bilibili.com/vide... 以上视频中,咱们着眼于可察看性和可监测性的关键点,例如日志(Logging),掂量指标(Metrics),链路追踪(Tracing),并深入分析了运行状况查看(Health checks)。 以下是视频中探讨的一些基本概念: 运行状况查看(Health checks)微服务实现了运行状况查看,最现实的状况是应用HTTP endpoints,以便各种实时监控零碎能够查问状态。 运行状况查看端点至多应做出以下响应: 零碎正在运行吗?它能够执行工作吗?在Kubernetes世界中,这些别离间接转换为liveness和readiness。 它们定义在Kubernetes的YAML部署配置文件中。 liveness门路是Kubernetes定期查问以查看故障的端点。 Kubernetes提供了liveness探针来监测失败的应用程序,并在它们不返回胜利代码时重新启动它们。readiness门路是Kubernetes查问以理解服务何时就绪,能够开始承受流量的终端。 当所有注册的查看都胜利时,它将返回HTTP状态代码200。ASP.NET Core提供用于向可监测性零碎报告运行状况的中间件和库,来提供运行状况查看。 相干文档请查阅ASP.NET Core中的运行状况查看。 日志无论您应用什么工具考察生产环境中的问题,最终都会是以日志的模式反馈问题的根本原因。 在分布式环境中,您须要确保日志记录蕴含有助于调试的深刻信息。 能够从一个集中的中央查问它们。 每个日志记录都须要有一个关联ID,以便进行跟踪以理解全局。 结构化日志应用结构化日志,您能够将序列化的对象增加到日志中,日志监视系统能够高效地查问这些对象。 例如,您能够依据customerID或trasnsactionID查问整个事务日志。 在ASP.NET Core应用程序中,能够应用提供结构化日志记录的Serilog。请查阅.NET Core和ASP.NET Core中的日志入门,以及Serilog理解结构化日志。 集中式日志和关联ID在传统应用程序中,日志文件存储在本地计算机上。在分布式环境中,把日志记录在某一台计算机中的纯文本文件中是没有帮忙的。生成日志的应用程序可能无法访问本地磁盘,或者当容器在虚拟机中挪动时,本地磁盘可能是高度瞬态的。因为在Cloud-native应用程序中应用基于文件的日志会遇到一些问题,因而首选集中式日志。日志由应用程序收集并传送到一个集中的日志应用程序,该应用程序对日志进行索引和存储。这类零碎每天能够接管数十GB的日志。Serilog提供了向集中式零碎(如Azure Application Insights,Azure Monitor的一项性能)写入日志事件的接收器。在构建跨多个服务的日志记录时,遵循一些规范做法也很有帮忙。例如,在事务开始时生成一个关联ID,而后将其记录到与该事务相干的每条音讯中,这样能够更容易地从集中式日志零碎中搜寻所有相干音讯。 分布式跟踪 分布式跟踪等效于古代云和微服务体系结构的调用堆栈,并增加了性能分析器。分布式跟踪或分布式申请跟踪有助于端到端查看申请,并使您可能从整体上辨认问题。跟踪能够为您提供无关问题的具体答案,例如事件产生在什么时候?它花了多少工夫?为什么要花这么长时间?哪些微服务解决了它?等等,像openzipkin / zipkin之类的开源分布式跟踪零碎,在该畛域十分风行。 为您的应用程序启用分布式跟踪就跟将相应的分布式跟踪提供商的SDK增加到每个微服务中一样简略。例如,在您的利用中装置并配置了Application Insights SDK后,SDK依赖关系主动收集器会主动收集风行框架,库和技术的跟踪信息。 在几个不同的零碎和工具之间,须要有一套规范以便于察看。OpenTelemetry标准化了不同的应用程序和框架如何收集和收回可观测性遥测。OpenTelemetry提供了一个与供应商无关的标准、一组api、sdk和工具以及用于可观测性遥测(分布式跟踪、度量等)的集成。查看博客文章OpenTelemetry .net reachs v1.0以获取详细信息。 入手模块咱们曾经构建了一系列模块来帮忙您学习构建.NET微服务和云原生技术。 查看以下模块,这些模块将帮忙您理解可监测性和可察看性相干技术。 行运行状况查看:创立和部署.Net微服务和云原生技术可监测性和可察看性:测试cloud-native ASP.NET Core微服务无关其余主题,请查看https://aka.ms/aspnet-microse...

May 27, 2021 · 1 min · jiezi

关于asp.net-core:手把手教你AspNetCore-WebApi认证与授权

前言这几天小明又有懊恼了,之前给小红的接口没有做认证受权,间接裸奔在线上,被马老板发现后狠狠的骂了一顿,连忙让小明把受权加上。连忙Baidu一下,发现大家都在用JWT认证受权,这个倒是挺适宜本人的。 什么是TokenToken是服务端生成的一串字符串,以作客户端进行申请的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,当前客户端只需带上这个Token前来申请数据即可,无需再次带上用户名和明码。 什么是JWTJson web token (JWT),是为了在网络应用环境间传递申明而执行的一种基于JSON的凋谢规范((RFC 7519).该token被设计为紧凑且平安的,特地实用于分布式站点的单点登录(SSO)场景。JWT的申明个别被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也能够减少一些额定的其它业务逻辑所必须的申明信息,该token也可间接被用于认证,也可被加密。 JWT认证流程 从图中能够看出次要有两局部组成:1、获取Token,2、通过Token进行受权。 应用JWT认证首先,装置JwtBearer包。 dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 3.1.0接下来,定义一个配置类,我这里为了简略间接用常量代替了,你也能够放在配置文件中。 public class TokenParameter{ public const string Issuer = "深度码农";//颁发者 public const string Audience = "深度码农";//接收者 public const string Secret = "1234567812345678";//签名秘钥 public const int AccessExpiration = 30;//AccessToken过期工夫(分钟)}接下来,定义一个通过用户名和明码,获取Token的控制器。 [Route("api/oauth")][ApiController]public class OAuthController : ControllerBase{ /// <summary> /// 获取Token /// </summary> /// <returns></returns> [HttpGet] [Route("token")] public ActionResult GetAccessToken(string username, string password) { //这儿在做用户的帐号密码校验。我这儿略过了。 if (username != "admin" || password != "admin") return BadRequest("Invalid Request"); var claims = new[] { new Claim(ClaimTypes.Name, username), new Claim(ClaimTypes.Role, ""), }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(TokenParameter.Secret)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var jwtToken = new JwtSecurityToken(TokenParameter.Issuer, TokenParameter.Audience, claims, expires: DateTime.UtcNow.AddMinutes(TokenParameter.AccessExpiration), signingCredentials: credentials); var token = new JwtSecurityTokenHandler().WriteToken(jwtToken); return Ok(token); }}接下来,增加Token身份认证到容器(Startup.ConfigureServices)。 ...

October 11, 2020 · 1 min · jiezi

关于asp.net-core:手把手教你AspNetCore-WebApi增删改查

前言小明曾经创立与运行了WebApi我的项目,理解我的项目构造有哪些组成,并学会了怎么公布到IIS。根底曾经建好,从当初开始要真正实现待办事项的性能了。 新建表CREATE TABLE [dbo].[Todo]( [Id] [uniqueidentifier] NOT NULL, [Name] [nvarchar](100) NULL, CONSTRAINT [PK_Todo] PRIMARY KEY CLUSTERED ( [Id] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]GO 增加模型类在“解决方案资源管理器”中,右键单击我的项目。 抉择“增加” > “新建文件夹”。 将文件夹命名为 Models。右键单击 Models 文件夹,而后抉择“增加” > “类” 。 将类命名为 Todo,而后抉择“增加”。using System;namespace App001.Models{ public class Todo { public Guid Id { get; set; } public string Name { get; set; } }}增加数据库上下文右键单击 Models 文件夹,而后抉择“增加” > “类” 。 将类命名为 TodoContext,而后单击“增加”。using Microsoft.EntityFrameworkCore;namespace App001.Models{ public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<Todo> Todos { get; set; } }}注册数据库上下文在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。 该容器向控制器提供服务。 ...

October 10, 2020 · 2 min · jiezi

关于asp.net-core:手把手教你AspNetCore-WebApi入门

需要前几天,马老板给小明和小红一个“待办事项”网站,小明负责后端,小红负责前端,并要求网站能够同时在 Windows、和 Linux 上运行。 小明整顿了一下“待办事项”的性能清单: 获取所有待办事项按 ID 获取项增加新项更新现有项删除项创立 Web 我的项目从“文件”菜单中选择“新建”>“我的项目” 。抉择“ASP.NET Core Web 应用程序”模板,再单击“下一步” 。将我的项目命名为 App001,而后单击“创立”。在“创立新的 ASP.NET Core Web 应用程序”对话框中,确认抉择“.NET Core”和“ASP.NET Core 3.1” 。 抉择“API”模板,而后单击“创立” 。 测试 API按 Ctrl+F5 运行利用。 Visual Studio 启动浏览器并导航到 https://localhost:<port>/WeatherForecast,其中 <port> 是随机抉择的端口号。 到目前为止,小明曾经胜利创立并运行了一个WebApi我的项目。 我的项目构造 从这个图中能够看出WebApi我的项目次要由Program.cs,Startup.cs,appsettings.json,WeatherForecastController.cs文件组成,那么当初咱们一个一个介绍一下这几个文件次要由什么作用。 Program类它是所有.net core程序的入口,定义了2个办法:Main() 和CreateHostBuilder(); public class Program{ public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });}代码不简单,创立泛型主机并运行。 ...

October 10, 2020 · 1 min · jiezi

ASP.NET Core 入门教程 10、ASP.NET Core 日志记录(NLog)入门

一、前言1、本教程主要内容ASP.NET Core + 内置日志组件记录控制台日志ASP.NET Core + NLog 按天记录本地日志ASP.NET Core + NLog 将日志按自定义LoggerName分类记录到不同目录ASP.NET Core + NLog 按文件大小归档记录本地日志NLog配置文件常用配置项说明2、本教程环境信息软件/环境说明操作系统Windows 10SDK2.1.401ASP.NET Core2.1.3IDEVisual Studio Code 1.33.0浏览器Chrome 73VS Code插件版本说明C#1.18.0提供C#智能感知, .NET Core 调试、编译等vscdoe-solution-explorer0.3.1提供解决方案视图XML Tools2.4.0提供XML高亮,格式化、XML树等本篇代码以下代码进行调整:https://github.com/ken-io/asp…二、ASP.NET Core 内置日志组件使用ASP.NET Core内置日志组件,可以将日志输出在控制台1、应用程序启动时配置日志修改Program.cs,在WebHostBuilder构建时配置日志//需要引入的命名空间using Microsoft.Extensions.Logging;public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .ConfigureLogging(logging => { logging.ClearProviders(); logging.SetMinimumLevel(LogLevel.Info); logging.AddConsole(); });配置项说明ClearProviders()清除日志提供程序,通常在引入第三方日志组件时使用SetMinimumLevel(LogLevel.Information)设置日志级别为InformationAddConsole()添加日志提供程序->控制台这里设置项,主要是SetMinimumLevel(LogLevel.Information),我们把日志级别设置为Information,可以减少很多控制台日志输出2、在控制中记录日志修改HomeController.cs,通过构造函数注入ILogger//需要引入的命名空间using Microsoft.Extensions.Logging;private readonly ILogger<HomeController> _logger;public HomeController(ILogger<HomeController> logger){ this._logger = logger;}public IActionResult Index(){ _logger.LogInformation("——\r\nindex:hello world\r\n——"); return Content(“Hello World ! -ken.io”);}public IActionResult CheckPhone(string phone){ _logger.LogInformation($"——\r\ncheck phone:{phone}\r\n——"); var result = true; var message = “pass”; if (string.IsNullOrWhiteSpace(phone)) { result = false; message = “phone number is empty”; _logger.LogError($"——\r\ncheck phone:{message}\r\n——"); } else if (phone.Length != 11) { result = false; message = “wrong phone number length”; _logger.LogWarning($"——\r\ncheck phone:{message}\r\n——"); } return Json(new { Result = result, Phone = phone, Message = message });}这里日志内容中包含的\r\n转义符在控制台输出时/写入文件时表示换行,这里加入\r\n和—主要是为了日志输出时,方便快速找到我们主动记录的日志。3、验证启动应用,访问 localhost:5001,会看到控制台输出了Hello World访问 localhost:5001/home/checkphone,将会看到Error日志访问 localhost:5001/home/checkphone?phone=000,将会看到警告日志访问 localhost:5001/home/checkphone?phone=16666666666,就只会看到Info级别日志了三、ASP.NET Core + NLog 记录本地日志1、安装NLog Package在控制台使用命令安装NLog包://进入项目目录cd Ken.Tutorial.Web//使用命令安装nlogdotnet add package NLog.Web.AspNetCore –version 4.8.1这里我安装的指定版本:4.8.1,如果不指定版本号,默认安装最新版本。2、引入NLog修改Program.cs,在WebHostBuilder构建时配置日志//需要引入的命名空间using NLog.Web;public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .ConfigureLogging(logging => { logging.ClearProviders(); logging.SetMinimumLevel(LogLevel.Info); logging.AddConsole(); }).UseNLog();//UseNLog3、配置NLog在项目根目录Ken.Tutorial.Web新建NLog配置文件nlog.config<?xml version=“1.0” encoding=“utf-8” ?><nlog xmlns=“http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" autoReload=“true”> <targets> <target name=“defaultlog” xsi:type=“File” keepFileOpen=“false” encoding=“utf-8” fileName="${basedir}/logs/${level}/${shortdate}.log” layout="${longdate}|${level:uppercase=true}|${logger}|${message}” /> </targets> <rules> <logger name="" minlevel=“trace” writeTo=“defaultlog” /> </rules></nlog>NLog配置文件一般主要包含两个部分,<targets>节点配置日志写入目标,<rules>节点配置匹配路由到<targets>的规则。主要配置项ken.io的说明${basedir}/logs/${level}/${shortdate}.log表示在程序运行目录,分日志级别按天写入日志文件${longdate}&#124;${level:uppercase=true}|${logger}&#124;${message}日志内容格式:时间+日志级别+LoggerName+日志内容<logger name="" minlevel=“trace” writeTo=“defaultlog” />支持将任意级别、任意LoggerName的日志写入target:defaultlog为了能让程序运行时能够读取到配置文件,需要修改Ken.Tutorial.Web.csproj文件,在生成运行文件到bin文件夹时,也把nlog.config复制过去。在工具栏切换到资源管理器视图,双击文件即可修改:<ItemGroup> <Content Update=“nlog.config” CopyToOutputDirectory=“PreserveNewest” /></ItemGroup>4、NLog测试启动应用,分别访问以下链接:localhost:5001/home/checkphonelocalhost:5001/home/checkphone?phone=000localhost:5001/home/checkphone?phone=16666666666然后在资源管理器视图下,查看bin/Debug目录,会看到日志已经按照配置文件的格式生成了对应的日志目录及文件。同时也会发现,nlog.config按照之前的配置也被复制到了程序运行目录。Info级别日志Warn级别日志Error级别日志这里Info日志是比较多的,我们可以通过配置rules,只输出程序本身主动记录的日志。<logger name=“Ken.Tutorial.*” minlevel=“trace” writeTo=“defaultlog” />四、NLog使用进阶1、通过自定义LoggerName归纳日志增加NLog配置,根据LoggerName创建目录<!–按照LoggerName分类–><target name=“customlog” xsi:type=“File” keepFileOpen=“false” encoding=“utf-8” fileName="${basedir}/logs/${logger}/${shortdate}-${level}.log" layout="${longdate}|${level:uppercase=true}|${logger}|${message}" /><!–记录LoggerName以log结尾的日志–><logger name="*log" minlevel=“trace” writeTo=“customlog” />在HomeController中添加测试归纳日志的Actionpublic IActionResult TestLog(){ var logger = NLog.LogManager.GetLogger(“testlog”); logger.Trace(“这是Trace日志”); logger.Debug(“这是Debug日志”); logger.Info(“这是Info日志”); logger.Warn(“这是警告日志”); logger.Error(“这是错误日志”); return Content(“ok”);}这里直接通过NLog.LogManager.GetLogger创建Logger,并没有使用内置日志Microsoft.Extensions.Logging.ILogger的实例,所以可以自定义LoggerName,另外,我们在应用启动时配置的最低日志级别等也不会对这种方式生效,是可以输出Trace、Debug级别的日志的。启动应用,访问:localhost:5001/home/testlog,然后就可以在资源管理器界面看到在logs文件夹下按照LoggerName生成的日志目录,并按照日志级别生成了不同的日志文件:2、按照日志文件大小归档日志增加NLog配置,按照日志文件大小归档<!–按照日志文件大小归档–><target name=“archivelog” xsi:type=“File” keepFileOpen=“false” encoding=“utf-8” fileName="${basedir}/logs/${logger}/${shortdate}-current.log" archiveFileName="${basedir}/logs/${logger}/${shortdate}.{####}.log" archiveAboveSize=“1024000” archiveNumbering=“Sequence” maxArchiveFiles=“100” layout="${longdate}|${level:uppercase=true}|${logger}|${message}" /> 这里配置当日志文件大小在超过约1mb的时候归档(这里偷懒用1000b代替1kb),最多归档100个日志文件,当归档日志文件超过100个时会把最早归档的日志删除。定义专属规则:<logger name=“logmany” minlevel=“trace” writeTo=“archivelog” />在HomeController中添加测试归档日志的Actionpublic IActionResult TestLogMany(){ var logger = NLog.LogManager.GetLogger(“logmany”); for (int i = 0; i <= 30000; i++) { logger.Info(“ASP.NET Core入门教程,这里是日志内容,测试NLog的日志归档功能,ken的杂谈(https://ken.io)”); } return Content(“ok”);}这里为了方便测试,直接用for循环,连续写入日志30000次启动应用,访问:localhost:5001/home/testlogmany,然后就可以在资源管理器界面看到在logs/logmany文件夹下按照文件大小归档的日志五、备注1、NLog常用配置项说明target节点/属性ken.io的说明target日志写入目标,可以配置写入类型、写入模板、文件名等nameTargetName,需要唯一,在rules节点引用xsi:type目标类型,支持文件(File)、数据库(Database)、邮件(Mail)keepFileOpen保持文件打开,不用每次写入日志时都打开、关闭文件,因为可以提高性能,默认值为:falseencoding文件编码,此处配置为:utf-8fileName日志文件名,包含日志完整路径和文件名,支持模板语法/变量archiveFileName归档日志文件名,包含日志完整路径和文件名,支持模板语法/变量archiveNumbering归档序号方式maxArchiveFiles最大归档日志文件数layout日志内容模板,内置了一些语法/变量模板语法/变量ken.io的说明${basedir}程序当前运行目录${level}日志级别${shortdate}当前日期,例如:2019-04-05${longdate}当前时间,精确到毫秒,例如:2019-04-05 14:10:22.4372${uppercase:${level}}把内容格式化成大写${logger}LoggerName,日志记录器名称,通常是Logger初始化所在类完整名称,例如:Ken.Tutorial.Web.Controllers.HomeController${machinename}机器名${message}日志内容rules节点/属性ken.io的说明logger日志记录器路由规则配置nameLoggerName匹配,支持完整匹配和模糊匹配,例如:mylog、Ken.*minlevel最低日志级别maxlevel最高日志级别level限制单一的日志级别levels指定一个或多个日志级别,用,间隔writeTo指定一个或多个target,用,间隔final在匹配到该规则之后不再匹配后续规则,默认值:falseenabled是否启用该规则,默认值:true2、附录本文代码示例https://github.com/ken-io/asp…本文参考https://docs.microsoft.com/zh…https://github.com/NLog/NLog....https://github.com/nlog/NLog/…本文后首发于我的独立博客:https://ken.io/note/asp.net-c… ...

April 8, 2019 · 2 min · jiezi

ASP.NET Core 入门教程 9、ASP.NET Core 中间件(Middleware)入门

一、前言1、本教程主要内容ASP.NET Core 中间件介绍通过自定义 ASP.NET Core 中间件实现请求验签2、本教程环境信息软件/环境说明操作系统Windows 10SDK2.1.401ASP.NET Core2.1.3MySQL8.0.xIDEVisual Studio Code 1.32.3浏览器Chrome 70VS Code插件版本说明C#1.17.1提供C#智能感知, .NET Core 调试、编译等vscdoe-solution-explorer0.3.1提供解决方案视图本篇代码以下代码进行调整:https://github.com/ken-io/asp…3、前置知识可能需要的前置知识C# 委托(Delegate)http://www.runoob.com/csharp/…C# 扩展方法https://docs.microsoft.com/zh…二、ASP.NET Core 中间件介绍1、ASP.NET Core 中间件基本说明当 ASP.NET Core MVC应用从Kestrel接收到请求,会建立HttpContext并交由Application来处理请求。在Application中会有一个处理该请求的通道,这就是ASP.NET Core 管道,通常称之为:请求处理管道在这个管道中,有一系列有序处理请求的组件,就是中间件(Middleware)。图中蓝色的部分可以认为是系统内置比较靠前的中间件或者我们自定义的中间件,MVC是一个特殊的中间件且通常放在最后,所以这里单独画出来对于MVC中间件,如果请求的URL与路由匹配,那么后面的中间件均不会生效。所以MVC通常放在最后。ASP.NET Core中会内置一些中间件,例如:身份验证、静态文件处理、MVC等。每个中间件在接受到请求后都可以选择是交由下一个中间件处理还是直接返回结果。例如:身份验证中间件验证未通过会直接引导到登陆页静态文件中间件判断为静态文件就会直接返回静态文件内容所以,中间件可以理解为请求处理管道中的请求处理器。我们也可以通过自定义中间件注册到管道中来干预请求。2、ASP.NET Core 中间件基础使用在程序中,中间件是基于委托来构建的。在应用启动时通过IApplicationBuilder注册到通道中。具体见启动类Startup.cs:public void Configure(IApplicationBuilder app, IHostingEnvironment env){ if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(routes => { //配置默认路由 routes.MapRoute( name: “Default”, template: “{controller}/{action}”, defaults: new { controller = “Home”, action = “Index” } ); });}UseDeveloperExceptionPage、UseMvc都是接口IApplicationBuilder的扩展方法。三、使用 ASP.NET Core 中间件实现请求验签如果你开发的API是为手机App服务的,那么你的API是一定要暴露给公网的,如果有人拿到API地址进行非法请求,获取用户信息或者是篡改数据,用户隐私、数据就会受到损害。这是很不安全的,我们可以让客户端请求的时候必须携带签名,在服务器端鉴权(验证签名)通过了再放行,这样就安全很多了。1、创建验签中间件在项目Ken.Tutorial.Web创建目录Middlewares,然后创建类:TokenCheckMiddleware.csusing System;using System.Security.Cryptography;using System.Text;using System.Threading.Tasks;using Microsoft.AspNetCore.Http;namespace Ken.Tutorial.Web.Middlewares{ public class TokenCheckMiddleware { private readonly RequestDelegate _next; public TokenCheckMiddleware(RequestDelegate requestDelegate) { this._next = requestDelegate; } public Task Invoke(HttpContext context) { //先从Url取token,如果取不到就从Form表单中取token var token = context.Request.Query[“token”].ToString() ?? context.Request.Form[“token”].ToString(); if (string.IsNullOrWhiteSpace(token)) { //如果没有获取到token信息,那么久返回token missing return context.Response.WriteAsync(“token missing”); } //获取前1分钟和当前的分钟 var minute0 = DateTime.Now.AddMinutes(-1).ToString(“yyyy-MM-dd HH:mm”); var minute = DateTime.Now.ToString(“yyyy-MM-dd HH:mm”); //当token和前一分钟或当前分钟任一时间字符串的MD5哈希一致,就认为是合法请求 if (token == MD5Hash(minute) || token == MD5Hash(minute0)) { return _next.Invoke(context); } //如果token未验证通过返回token error return context.Response.WriteAsync(“token error”); } public string MD5Hash(string value) { using (var md5 = MD5.Create()) { var result = md5.ComputeHash(Encoding.ASCII.GetBytes(value)); var strResult = BitConverter.ToString(result); return strResult.Replace("-", “”); } } }}由于是侧重自定义中间件,所有验签的逻辑就写的非常简单,如果实际项目使用,可以按照自己需求调整2、创建扩展方法在Middlewares目录下新建类:MiddlewareExtension.csusing Microsoft.AspNetCore.Builder;namespace Ken.Tutorial.Web.Middlewares{ public static class MiddlewareExtension { public static IApplicationBuilder UseTokenCheck(this IApplicationBuilder builder) { return builder.UseMiddleware<TokenCheckMiddleware>(); } }}这里我们通过扩展方法,将TokenCheckMiddleware挂在接口IApplicationBuilder上3、中间件注册/引用在启动类Startup.cs的Configure方法中注册/引用中间件public void Configure(IApplicationBuilder app, IHostingEnvironment env){ //省略部分代码 app.UseTokenCheck(); app.UseMvc(routes => { //省略路由配置代码 });}如果你觉得扩展方法有点多余,也可以直接使用UseMiddleware方法注册public void Configure(IApplicationBuilder app, IHostingEnvironment env){ //省略部分代码 app.UseMiddleware<TokenCheckMiddleware>(); app.UseMvc(routes => { //省略路由配置代码 });} 这里要注意的是,如果你是一个MVC应用,请一定要把MVC这个中间件作为最后一个注册。因为中间件是按照注册顺序被调用的。如果放在MVC之后,请求的URL也有对应路由适配,那么整个请求已经被MVC接管。后面的中间件就不会被调用了。4、验签中间件测试启动应用,然后验证不同情况下的访问结果URLResponselocalhost:5001token missinglocalhost:5001?token=testtoken errorlocalhost:5001?token=3D76FEA1D0ADD0C7639B73023436C6EAHello World ! -ken.io为了方便测试,MD5哈希的值我们可以在线生成:ttp://tool.chinaz.com/tools/md5.aspx把当前分钟,例如:2019-03-27 23:23 通过MD5在线生成那就是3D76FEA1D0ADD0C7639B73023436C6EA四、备注本文代码示例https://github.com/ken-io/asp…本文参考https://docs.microsoft.com/zh…延伸阅读https://www.cnblogs.com/artec…本文首发于我的独立博客:https://ken.io/note/asp.net-c… ...

April 1, 2019 · 1 min · jiezi

ASP.NET Core 入门教程 8、ASP.NET Core + Entity Framework Core 数据访问入门

一、前言1、本教程主要内容ASP.NET Core MVC 集成 EF Core 介绍&操作步骤ASP.NET Core MVC 使用 EF Core + Linq to Entity 访问MySQL数据库ASP.NET Core MVC 使用 EF Core + 原生SQL访问MySql数据库EF Core + MySQL数据库插入数据后获取自增列的值Entity Framework Core 简称为 EF Core2、本教程环境信息软件/环境说明操作系统Windows 10SDK2.1.401ASP.NET Core2.1.3MySQL8.0.xIDEVisual Studio Code 1.30浏览器Chrome 70VS Code插件版本说明C#1.17.1提供C#智能感知, .NET Core 调试、编译等vscdoe-solution-explorer0.3.1提供解决方案视图本篇代码以下代码进行调整:https://github.com/ken-io/asp…3、准备工作安装MySQL数据库:https://dev.mysql.com/downloa…Windows环境下载安装包后双击安装,一直下一步即可。MacOS环境参考:https://ken.io/note/macos-mys…CentOS环境参考:https://ken.io/note/centos-my…4、前置知识控制反转(IOC)原则与依赖注入(DI)ASP.NET Core 默认集成了DI。所有官方模块的引入都要使用DI的方式引入。https://baike.baidu.com/item/IOCLinq使用教程https://docs.microsoft.com/zh…二、EF Core + MySQL 前置准备EF Core 全称:Entity Framework Core,为微软为 .NET Core平台开发的ORM框架。对应是 .NET Framework平台的 Entity Framework(EF),无论是EF还是EF Core都可以说是 .NET 平台开发效率最高的ORM框架。1、引入 EF Core + MySQL ProviderEF Core已经集成在 ASP.NET Core 中,但默认并不支持MySQL,如果需要连接MySQL,需要添加MySQL相关的Provider,这里我选择的是:Pomelo.EntityFrameworkCore.MySql任意命令行操作即可,我用的是VS Code自带的命令行//进入项目根目录: Ken.Tutorial.Webcd Ken.Tutorial.Web//添加Packagedotnet add package Pomelo.EntityFrameworkCore.MySql这里我添加的 MySql.Data.EntityFrameworkCore 版本是 8.0.13,如果你想跟我使用一样的版本,可以使用以下命令:dotnet add package Pomelo.EntityFrameworkCore.MySql –version 2.1.42、创建MySQL库表创建数据库CREATE DATABASE ken_tutorial;创建表USE ken_tutorial;DROP TABLE IF EXISTS user;CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(255) DEFAULT NULL, age int(11) DEFAULT NULL, hobby varchar(500) DEFAULT NULL, PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;3、配置链接字符串在项目根目录Ken.Tutorial.Web中创建配置文件appsettings.json,并写入以下配置{ “ConnectionStrings”: { “testdb”: “server=localhost;database=Ken.Tutorial;uid=root;pwd=root;” }}4、实体&DbContext准备创建user表对应实体在项目根目录Ken.Tutorial.Web中创建目录Models,并在其中创建类:UserEntity.csusing System;using System.ComponentModel.DataAnnotations;using System.ComponentModel.DataAnnotations.Schema;namespace Ken.Tutorial.Web.Models{ [Table(“user”)] [Serializable] public class UserEntity { [Key]//主键 [DatabaseGenerated(DatabaseGeneratedOption.Identity)]//自增列 [Column(“id”)] public int Id { get; set; } [Column(“name”)] public string Name { get; set; } [Column(“age”)] public int Age { get; set; } [Column(“hobby”)] public string Hobby { get; set; } }}创建DBContext在项目根目录Ken.Tutorial.Web中创建目录Repositories,并在其中创建类:TutorialDbContext.csusing System;using Microsoft.EntityFrameworkCore;using Microsoft.Extensions.Configuration;namespace Ken.Tutorial.Web.Repositories{ public class TutorialDbContext : DbContext { private IConfiguration Configuration { get; } public TutorialDbContext(IConfiguration configuration) { this.Configuration = configuration; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseMySql(Configuration.GetConnectionString(“testdb”)); } ppublic DbSet<UserEntity> Users { get; set; } }}TutorialDbContext 继承了 DbContext,然后再Context初始化时配置MySQL链接。由于 ASP.NET Core 默认使用了DI组件,所以我们取配置文件,就需要在构造函数中获取 IConfiguration 注入的实例。在Startup.cs配置注入,以便数据访问类可以访问DI方式获取TutorialDbContextpublic void ConfigureServices(IServiceCollection services){ //引入MVC模块 services.AddMvc(); //配置DbContext注入 services.AddTransient<TutorialDbContext>();}三、EF Core + Linq to entity 访问数据库1、创建Linq To Entity 数据访问类using System;using System.Collections.Generic;using System.Linq;using Ken.Tutorial.Web.Models;namespace Ken.Tutorial.Web.Repositories{ public class TutorialRepository { private TutorialDbContext DbContext { get; } public TutorialRepository(TutorialDbContext dbContext) { //在构造函数中注入DbContext this.DbContext = dbContext; } //添加 public int Add(UserEntity user) { using (DbContext) { //由于我们在UserEntity.Id配置了自增列的Attribute,EF执行完成后会自动把自增列的值赋值给user.Id DbContext.Users.Add(user); return DbContext.SaveChanges(); } } //删除 public int Delete(int id) { using (DbContext) { var userFromContext = DbContext.Users.FirstOrDefault(u => u.Id == id); DbContext.Users.Remove(userFromContext); return DbContext.SaveChanges(); } } //更新 public int Update(UserEntity user) { using (DbContext) { var userFromContext = DbContext.Users.FirstOrDefault(u => u.Id == user.Id); userFromContext.Name = user.Name; userFromContext.Age = user.Age; userFromContext.Hobby = user.Hobby; return DbContext.SaveChanges(); } } //查询 public UserEntity QueryById(int id) { using (DbContext) { return DbContext.Users.FirstOrDefault(u => u.Id == id); } } //查询集合 public List<UserEntity> QueryByAge(int age) { using (DbContext) { return DbContext.Users.Where(u => u.Age == age).ToList(); } } //查看指定列 public List<string> QueryNameByAge(int age) { using (DbContext) { return DbContext.Users.Where(u => u.Age == age).Select(u => u.Name).ToList(); } } //分页查询 public List<UserEntity> QueryUserPaging(int pageSize, int page) { using (DbContext) { return DbContext.Users.Skip(pageSize * (page - 1)).Take(pageSize).ToList(); } } //事务:将年龄<0的用户修改年龄为0 public int FixAge() { using (DbContext) { using (var transaction = DbContext.Database.BeginTransaction()) { try { var userListFromContext = DbContext.Users.Where(u => u.Age < 0); foreach (UserEntity u in userListFromContext) { u.Age = 0; } var count = DbContext.SaveChanges(); transaction.Commit(); return count; } catch { transaction.Rollback(); return 0; } } } } }}2、创建测试API在Controllers文件夹下创建EfCoreController.cs作为访问入口using System;using Ken.Tutorial.Web.Models;using Ken.Tutorial.Web.Repositories;using Microsoft.AspNetCore.Mvc;namespace Ken.Tutorial.Web.Controllers{ public class EfCoreController : Controller { public TutorialRepository Repository { get; } public EfCoreController(TutorialRepository repository) { this.Repository = repository; } public IActionResult Add(UserEntity user) { var message = Repository.Add(user) > 0 ? “success” : “failed”; return Json(new { Message = message, User = user }); } public IActionResult Delete(int id) { var message = Repository.Delete(id) > 0 ? “success” : “failed”; return Json(new { Message = message }); } public IActionResult Update(UserEntity user) { var message = Repository.Update(user) > 0 ? “success” : “failed”; return Json(new { Message = message, User = user }); } public IActionResult QueryById(int id) { var user = Repository.QueryById(id); return Json(new { User = user }); } public IActionResult QueryByAge(int age) { var users = Repository.QueryByAge(age); return Json(new { Users = users }); } public IActionResult QueryNameByAge(int age) { var users = Repository.QueryNameByAge(age); return Json(new { Users = users }); } public IActionResult QueryUserPaging(int pageSize, int page) { var users = Repository.QueryUserPaging(pageSize, page); return Json(new { Users = users }); } public IActionResult FixAge() { var count = Repository.FixAge(); return Json(new { FixCount = count }); } }}3、启动测试在Startup.cs中配置Repository注入public void ConfigureServices(IServiceCollection services){ //其他代码省略 //配置Repository注入 services.AddTransient<TutorialRepository>();}启动项目并测试API示例添加用户/efcore/add?name=ken&age=18&hobby=coding删除用户/efcore/delete?id=0更新用户/efcore/update?id=1&name=ken&age=666&hobby=codingOrGaming查询单个用户/efcore/querybyid?id=0查询多个用户/efcore/querybyage?age=18查询多个用户名/efcore/querynamebyage?age=18分页查询用户/efcore/queryuserpaging?pagesize=3&page=1修复异常年龄/efcore/fixage四、EF Core + 原生SQL 访问数据库1、创建EF Core + 原生SQL 数据访问类using System;using System.Collections.Generic;using System.Data;using System.Data.SqlClient;using System.Linq;using Ken.Tutorial.Web.Models;using Microsoft.EntityFrameworkCore;using MySql.Data.MySqlClient;namespace Ken.Tutorial.Web.Repositories{ public class TutorialWithSqlRepository { private TutorialDbContext DbContext { get; } public TutorialWithSqlRepository(TutorialDbContext dbContext) { //在构造函数中注入DbContext this.DbContext = dbContext; } //添加 public int Add(UserEntity user) { using (var connection = DbContext.Database.GetDbConnection()) { connection.Open(); var command = connection.CreateCommand() as MySqlCommand; command.CommandText = “INSERT INTO user (name,age,hobby) VALUES(@name,@age,@hobby)”; command.Parameters.Add(new MySqlParameter() { ParameterName = “@name”, DbType = DbType.String, Value = user.Name }); command.Parameters.Add(new MySqlParameter() { ParameterName = “@age”, DbType = DbType.Int32, Value = user.Age }); command.Parameters.Add(new MySqlParameter() { ParameterName = “@hobby”, DbType = DbType.String, Value = user.Hobby }); var count = command.ExecuteNonQuery(); //获取插入时产生的自增列Id并赋值给user.Id使用 user.Id = (int)command.LastInsertedId; return count; } } //删除 public int Delete(int id) { using (DbContext) { return DbContext.Database.ExecuteSqlCommand( “DELETE FROM user WHERE id={0}”, id); } } //更新 public int Update(UserEntity user) { using (DbContext) { return DbContext.Database.ExecuteSqlCommand( “UPDATE user SET name={0}, age={1}, hobby={2} WHERE id={3}”, user.Name, user.Age, user.Hobby, user.Id); } } //查询 public UserEntity QueryById(int id) { using (DbContext) { return DbContext.Users.FromSql(“SELECT id,name,age,hobby FROM user WHERE id={0}”, id).FirstOrDefault(); } } //查询集合 public List<UserEntity> QueryByAge(int age) { using (DbContext) { return DbContext.Users.FromSql(“SELECT id,name,age,hobby FROM user WHERE age={0}”, age).ToList(); } } //查看指定列 public List<string> QueryNameByAge(int age) { using (DbContext) { return DbContext.Users.FromSql(“SELECT id,name FROM user WHERE age={0}”, age).Select(u => u.Name).ToList(); } } //分页查询 public List<UserEntity> QueryUserPaging(int pageSize, int page) { using (DbContext) { return DbContext.Users.FromSql(“SELECT id,name,age,hobby FROM user LIMIT {0},{1}”, pageSize * (page - 1), pageSize).ToList(); } } //事务:将年龄<0的用户修改年龄为0 public int FixAge() { using (DbContext) { using (var connection = DbContext.Database.GetDbConnection()) { //打开连接 connection.Open(); //开启事务 using (var transaction = connection.BeginTransaction()) { try { //获取命令对象 var command = connection.CreateCommand(); command.Transaction = transaction; command.CommandText = “UPDATE user SET age=@age WHERE age<@age”; command.Parameters.Add(new MySqlParameter() { ParameterName = “@age”, DbType = DbType.Int32, Value = 0 }); var count = command.ExecuteNonQuery(); transaction.Commit(); return count; } catch (Exception ex) { connection.Close(); transaction.Rollback(); return 0; } } } } } }}2、创建测试API在Controllers文件夹下创建EfCoreWithSqlController.cs作为访问入口using System;using Ken.Tutorial.Web.Models;using Ken.Tutorial.Web.Repositories;using Microsoft.AspNetCore.Mvc;namespace Ken.Tutorial.Web.Controllers{ public class EfCoreWithSqlController : Controller { public TutorialWithSqlRepository Repository { get; } public EfCoreWithSqlController(TutorialWithSqlRepository repository) { this.Repository = repository; } public IActionResult Add(UserEntity user) { var message = Repository.Add(user) > 0 ? “success” : “failed”; return Json(new { Message = message, User = user }); } public IActionResult Delete(int id) { var message = Repository.Delete(id) > 0 ? “success” : “failed”; return Json(new { Message = message }); } public IActionResult Update(UserEntity user) { var message = Repository.Update(user) > 0 ? “success” : “failed”; return Json(new { Message = message, User = user }); } public IActionResult QueryById(int id) { var user = Repository.QueryById(id); return Json(new { User = user }); } public IActionResult QueryByAge(int age) { var users = Repository.QueryByAge(age); return Json(new { Users = users }); } public IActionResult QueryNameByAge(int age) { var users = Repository.QueryNameByAge(age); return Json(new { Users = users }); } public IActionResult QueryUserPaging(int pageSize, int page) { var users = Repository.QueryUserPaging(pageSize, page); return Json(new { Users = users }); } public IActionResult FixAge() { var count = Repository.FixAge(); return Json(new { FixCount = count }); } }}3、启动测试在Startup.cs中配置Repository注入public void ConfigureServices(IServiceCollection services){ //其他代码省略 //配置Repository注入 services.AddTransient<TutorialWithSqlRepository>();}启动项目并测试API示例添加用户/efcorewithsql/add?name=ken&age=18&hobby=coding删除用户/efcorewithsql/delete?id=0更新用户/efcorewithsql/update?id=1&name=ken&age=666&hobby=codingOrGaming查询单个用户/efcorewithsql/querybyid?id=0查询多个用户/efcorewithsql/querybyage?age=18查询多个用户名/efcorewithsql/querynamebyage?age=18分页查询用户/efcorewithsql/queryuserpaging?pagesize=3&page=1修复异常年龄/efcorewithsql/fixage五、备注1、附录本文代码示例https://github.com/ken-io/asp…本文参考https://docs.microsoft.com/zh… https://www.learnentityframew… https://mysql-net.github.io/M…本文首发于我的独立博客:https://ken.io/note/asp.net-c… ...

December 29, 2018 · 6 min · jiezi

ASP.NET Core 入门教程 7、ASP.NET Core MVC 分部视图入门

一、前言1、本教程主要内容ASP.NET Core MVC (Razor)分部视图简介ASP.NET Core MVC (Razor)分部视图基础教程ASP.NET Core MVC (Razor)强类型分部视图教程2、本教程环境信息软件/环境说明操作系统Windows 10SDK2.1.401ASP.NET Core2.1.3IDEVisual Studio Code 1.30浏览器Chrome 70本篇代码以下代码进行调整:https://github.com/ken-io/asp…3、准备工作VS Code 本身不提供 ASP.NET Core MVC 视图引擎(Razor)的智能感知。 幸运的是,VS Code C#扩展 从 1.17.0 版本开始支持Razor视图引擎的智能感知。所以,我们要将VS Code C#扩展升级到最新版本。另外,要特意说明的是,在VS Code 1.30版本,解决方案(Solution)视图的视图入口改到了侧边工具栏二、ASP.NET Core MVC (Razor)分部视图简介1、Razor分部视图概述在Razor视图引擎中,我们可以定义.cshtml文件作为“视图”来渲染需要呈现给用户的内容。对于所有页面共用的部分,我们可以定义母版页(Layout)让视图继承共用的部分。当有些公共的部分我们只在某些页面用到,不需要每个页面都用到。或者这个公共的内容需要作为模板使用多次,母版页就不适合承担这样的作用。这时候我们可以使用分部视图来实现。2、Razor分部视图定义与引用Razor分部视图定义视图与分部视图在定义上并没有本质的不同,均是创建.cshtml文件作为视图使用,只是在渲染的时候作为分部视图来渲染/加载。在之前提到过,通常公共的Razor视图文件名都以_开头并放在/Views/Shared文件夹中,分部视图也不例外。例如:/Views/Shared/_PartialViewTest.cshtml 如果分部视图只在某个控制器返回的视图中引用,也可以创建在该控制器对应的视图目录。例如:/Views/Home/_PartialViewTest.cshtmlRazor分部视图引用//同步引用@Html.Partial("_PartialViewTest")//异步引用(官方推荐)@await Html.PartialAsync("_PartialViewTest")微软官方更推荐使用异步加载的方式,因为同步加载可能会出现程序死锁的情况如果没有使用异步方式,会收到编译器警告:warning MVC1000: Use of IHtmlHelper.Partial may result in application deadlocks. Consider using <partial> Tag Helper or IHtmlHelper.PartialAsync.如果你非常在意性能,也可以使用 Html.RenderPartialAsync 呈现分部视图。 这种方式会直接呈现分部视图的内容,而不会组装成 IHtmlContent 对象放回。@{ await Html.RenderPartialAsync("_PartialViewTest");}由于 Html.RenderPartialAsync并不会返回任何内容,所以需要在Razor语句块中调用Razor分部视图查找顺序同视图相同:Views/[ControllerName]/[PartialViewName].cshtmlViews/Shared/[PartialViewName].cshtml当然,你也可以直接指定完整路径,例如:@await Html.PartialAsync("/Views/Home/_PartialViewTest.cshtml")三、 Razor分部视图基础使用1、定义分部视图在/Views/Shared目录下创建视图 ‘_DateTimeInfo.cshtml’当前时间:@DateTime.Now.ToString(“yyyy-MM-dd HH:mm:ss:fff”) 当前星期:@DateTime.Now.DayOfWeek2、创建视图并引用分部视图在/Views目录下创建目录Partial,并在/Views/Partial 目录下创建文件 Demo.cshtml@{ ViewBag.Title = “PartialView Demo”;}<h3>@ViewBag.Title</h3><p>PartialView Demo by ken.io</p>@Html.Partial("_DateTimeInfo")<hr/>@await Html.PartialAsync("_DateTimeInfo")3、创建控制器在 /Controllers 目录下创建PartialController.cs并创建对应Actionusing System;using Microsoft.AspNetCore.Mvc;namespace Ken.Tutorial.Web.Controllers{ public class PartialController : Controller { public IActionResult Demo() { return View(); } }}4、访问测试启动项目,访问 /partial/demo ,将会看到四、带参数的Razor分部视图1、视图对象准备在项目根目录中创建模型目录Models,并在下面创建对象NoteViewModel.csusing System;namespace Ken.Tutorial.Web.Models{ public class NoteViewModel { public string Title { get; set; } public DateTime PublishTime { get; set; } public string Body { get; set; } }}2、定义分部视图在/Views/Shared目录下创建视图 ‘_NoteInfo.cshtml’@model Ken.Tutorial.Web.Models.NoteViewModel;<h3>@Model.Title</h3><span>@Model.PublishTime.ToString(“yyyy-MM-dd”)</span><p>@Model.Body</p>实际上就是创建强类型分部视图:-D3、创建视图并引用分部视图在/Views/Partial 目录下创建文件 DemoWithParams.cshtml@using Ken.Tutorial.Web.Models;@{ ViewBag.Title = “PartialView With Params Demo”;}<h3>@ViewBag.Title</h3><p>PartialView With Params Demo by ken.io</p>@await Html.PartialAsync("_NoteInfo", new NoteViewModel() { Title = “这是一个分部视图测试笔记”, PublishTime = DateTime.Now, Body = “这是笔记的内容” })4、在控制器中编写对应Action在控制器 PartialController.cs 中增加以下 Action:public IActionResult DemoWithParams(){ return View();}5、访问测试启动项目,访问 /partial/demowithparams ,将会看到如果是文章列表页,用起来会显得更方便。五、备注1、附录本文代码示例https://github.com/ken-io/asp…本文参考https://docs.microsoft.com/zh…本文首发于我的独立博客:https://ken.io/note/asp.net-c… ...

December 21, 2018 · 1 min · jiezi

开源干货!.NET Core + Vue.js通用动态权限(RBAC)管理系统框架[DncZeus]开源

DncZeus前言关于 DncZeusDncZeus = Dnc + Zeus"Dnc"–.Net Core 的缩写;“Zeus”–中文译为宙斯,是古希腊神话中的众神之王,奥林匹斯十二主神之首,统治宇宙万物的至高无上的主神(在古希腊神话中主神专指宙斯),人们常用“众神和人类的父亲”、“神王”来称呼他,是希腊神话诸神中最伟大的神。DncZeus的愿景就是做一个.NET Core 领域的简易精致的通用后台权限管理模板系统基础框架,努力向.NET Core 领域的"宙斯"看齐。项目简介DncZeus是一个基于 ASP.NET Core 2 + Vue.js 的前后端分离的通用后台管理系统框架。后端使用.NET Core 2 + Entity Framework Core 构建,UI 则是目前流行的基于 Vue.js 的 iView。项目实现了前后端的动态权限管理和控制以及基于 JWT 的用户令牌认证机制,让前后端的交互更流畅。DncZeus并不是一个完整的业务系统,但她提供完成业务系统的绝大多数开发场景,让每一位.NET 开发者都能基于DncZeus快速开发出交互、体验以及功能具佳的.NET Core 单页应用程序(SPA)。支持DncZeus(求Start :))如果你觉得DncZeus对你或者他人有用,请为DncZeus点个赞,求扩散,让更多人获得帮助!!!在线体验(Demo)超级管理员:administrator 管理员:admin密码:111111地址:https://dnczeus.codedefault.com由于是个人项目,资金有限,体验服是低配,请大家爱惜,轻戳,不胜感激!!!适合人群由于 DncZeus 考虑到初级.NET 开发者都可以使用,所以后端项目未涉及过多架构和封装(代码逻辑一目了然),但为了你更好地熟悉和运用 DncZeus,你需要了解:ASP.NET CoreVue.jsiViewASP.NET Core 的知识能确保你可以看懂和了解后端是如何实现和工作的,而 Vue.js 框架则是前端实现的基石,当然 iView 这个基于 Vue.js 的 UI 框架也是必须要了解的,因为 DncZeus 正是基于 [iview-admin]1来实现的前端 UI 交互。如果你对这两个方面的知识还不熟悉,建议你可以先学习一些理论再来运用 DncZeus 这个框架。关于 ASP.NET Core 和 Vue.js 的入门请参考:ASP.NET Core 官方文档Vue.js 官方文档环境和工具Node.js(同时安装 npm 前端包管理工具)Visual Studio 2017(15.8.8 或者以上版本)VS Code 或者其他前端开发工具git 管理工具SQL Server CE 或者 SQL Server Express 或者 SQL Server 2014 +技术实现ASP.NET Core 2(.NET Core 2.1.502)ASP.NET WebApi CoreJWT 令牌认证AutoMapperEntity Framework Core 2.0.NET Core 依赖注入Swagger UIVue.js(ES6 语法)iView(基于 Vue.js 的 UI 框架)下载项目使用Git工具下载首先请确保你本地开发环境已安装了git管理工具,然后在需要存放本项目的目录打开git命令行工具Git Bash Here,在命令行中输入如下命令:git clone https://github.com/lampo1024/DncZeus.git以上命令就把DncZeus的远程代码拉取到你的本地开发机上。手动下载如果你不愿意使用git管理工具下载DncZeus的远程代码,你也可以在github托管地址手动下载,打开地址https://github.com/lampo1024/…,找到页面中的按钮"Clone or download",如下图示:在弹出的对话框中点击按钮"Download ZIP"即可开始下载DncZeus的源代码,如下图:安装依赖前端项目在将DncZeus的源代码下载到本地之后,如果你使用的git管理工具,可以不用退出当前的git管理工具,输入如下命令:cd DncZeus/DncZeus.App进入到DncZeus的前端项目目录DncZeus.App。在命令行中输入如下命令进行前端依赖包的还原操作:npm install或者npm i后端项目在Visual Studio中打开解决方案[DncZeus.sln]。首先根据自己的开发环境(SQL Server数据库类型,本示例默认是SQL Server Localdb)修改配置文件appsettings.json中的数据库连接字符串,示例默认连接字符串为:“ConnectionStrings”: { “DefaultConnection”: “Server=(localdb)\mssqllocaldb;Database=DncZeus;Trusted_Connection=True;MultipleActiveResultSets=true” }再打开包管理控制台(Package Manager Console),执行如下命令生成数据库表结构:Update-Database -verbose最后,打开项目根目录中的脚本文件夹[Scripts],执行脚本文件[Init_data.sql]以初始化系统数据。恭喜你,到这里所有的准备工作就完成了。赶紧体验DncZeus框架吧!!!运行使用Visual Studio开发工具打开DncZeus根目录中的VS解决方案文件DncZeus.sln,设置DncZeus.Api项目为默认启动项并运行此项目。这时在浏览器中打开地址:http://localhost:54321/swagger ,便可以查看到DncZeus已经实现的后端API接口服务了。在命令行中进入到DncZeus的前端项目目录[DncZeus.App],运行如下命令以启动前端项目服务:npm run dev成功运行后会自动在浏览器中打开地址: http://localhost:9000使用和授权DncZeus项目是一个开源项目,你可以直接基于本项目进行扩展或者二次开发,也可以修改其中的代码。但请保留原文件中的版权信息,尊重本人的劳动成果,违者必究,谢谢合作。问题与反馈遇到问题怎么办?直接提交issueQQ群:483350228码友网 ...

December 21, 2018 · 1 min · jiezi