如何优雅使用 Git 版本控制

Git 简介 Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 克隆存储库 将远程存储库克隆到本地,在本地进行修改,然后提交到远程存储库。 创建存储库 在本地创建存储库,然后将本地存储库推送到远程存储库。关于代码存储库的命名规范使用,使用 KebabCaseLower 命名法。 Naming policy Original Converted PascalCase HelloShop HelloShop CamelCase HelloShop helloShop SnakeCaseLower HelloShop hello_shop SnakeCaseUpper HelloShop HELLO_SHOP KebabCaseLower HelloShop hello-shop KebabCaseUpper HelloShop HELLO-SHOP 本地仓库设置 Git 设置包括全局设置和存储库设置,可设置多个远程存储库。 说明文件 在存储库中创建一个名为 README.md 的文件,用于存放存储库的说明文档,当然如果可能每个文件夹都应该有一个说明文档。 忽略文件 gitignore 文件的作用是指定不需要提交到代码存储库的文件,例如编译后的文件、日志文件等。 https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 常规操作 提取、拉取、提交、推送、同步、回滚、标签、冲突、暂存(行)、拉取。 分支管理 存储库分支用于管理存储库的版本,例如 master,develop,release,hotfix, feature 等。主要操作包括:创建分支,切换分支,合并分支,删除分支。 存储库管理 浏览存储库、管理存储库、使用多个存储库、解决合并冲突。

2026年3月10日

设计权限定义和权限提供者

权限定义 每个微服务是自治的,权限定义也是每个微服务的职责,各自定义自己的权限,权限管理器会自动合并权限定义,权限定义上下文是微服务内的权限定义和全局权限定义的合集,权限提供者会根据权限定义上下文提供权限数据,权限分组是为了方便权限定义的管理,权限分组是权限定义的一部分,权限提供者自动注入到容器,提供权限查询端点。 架构设计 权限扩展方法 IServiceCollection AddPermissionDefinitions(this IServiceCollection services) EndpointRouteBuilder MapPermissionDefinitions(this IEndpointRouteBuilder endpoints, params string[] tags)

2026年3月10日

身份认证系统

ASP.NET Core Identity 体系结构 ASP.NET Core Identity 是一个成熟的身份认证系统,它提供了用户管理、角色管理、声明管理、密码管理、登录管理、外部登录管理、双重身份认证、电子邮件确认、电话号码确认、安全令牌管理、用户锁定、用户解锁、用户注销、用户删除、用户恢复等功能。 Identity 体系结构说明 实体类型 说明 User 表示用户。 Role 表示角色。 UserClaim 表示用户拥有的声明。 UserToken 表示用户的身份验证令牌。 UserLogin 将用户与登录名相关联。 RoleClaim 表示向角色内的所有用户授予的声明。 UserRole 关联用户和角色的联接实体。 使用 EfCore 存储 Identity 数据 定义实体类型 public class User : IdentityUser<int> { public DateTimeOffset CreationTime { get; set; } = DateTimeOffset.UtcNow; } public class Role : IdentityRole<int> { public DateTimeOffset CreationTime { get; set; } = DateTimeOffset.UtcNow; } 配置实体类型 public class UserEntityTypeConfiguration : IEntityTypeConfiguration<User> { public void Configure(EntityTypeBuilder<User> builder) { builder.ToTable("Users"); builder.HasKey(x => x.Id); builder.Property(x => x.CreationTime); } } 复用 Identity 数据库上下文 dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore public class IdentityServiceDbContext(DbContextOptions<IdentityServiceDbContext> options) : IdentityDbContext<User, Role, int>(options) { protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true); } } 使用 Identity API 终结点 builder.Services.AddIdentityApiEndpoints<User>(options => { options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequiredLength = 5; options.SignIn.RequireConfirmedAccount = false; }).AddEntityFrameworkStores<IdentityServiceDbContext>(); app.MapGroup("identity").MapIdentityApi<User>(); 操作 Identity 数据 UserManager、RoleManager、SignInManager、PasswordValidator 等服务都可以用来操作 Identity 数据,没有特殊情况不建议使用 DbContext 直接操作 Identity 数据。 ...

2026年3月10日

实现 CQRS 中的 Query 模式

在 CQRS 模式的全程是 Command Query Responsibility Segregation,即命令查询职责分离。在 CQRS 模式中,命令和查询是分开实现的,命令负责写操作,查询负责读操作,这样可以更好地实现单一职责原则,同时也可以更好地实现性能优化。 https://learn.microsoft.com/zh-cn/azure/architecture/patterns/cqrs https://www.xcode.me/Training/UnitNote/576 定义订单查询接口 public interface IOrderQueries { Task<OrderDetails> GetOrderAsync(int id); } 实现订单查询接口 public class OrderQueries : IOrderQueries { private readonly OrderingContext _context; public OrderQueries(OrderingContext context) { _context = context; dbContext.Database.SetConnectionString("db2_connection_string"); } public async Task<OrderDetails> GetOrderAsync(int id) { // do something } } 注册订单查询接口 services.AddScoped<IOrderQueries, OrderQueries>(); 使用订单查询接口 public class OrderController : ControllerBase { private readonly IOrderQueries _orderQueries; public OrderController(IOrderQueries orderQueries) { _orderQueries = orderQueries; } [HttpGet("{id}")] public async Task<IActionResult> GetOrderAsync(int id) { var order = await _orderQueries.GetOrderAsync(id); return Ok(order); } }

2026年3月10日

实现产品管理微服务

创建实体 HelloWorld.ProductService.Entities.Products Product & CatalogBrand EfCore 使用 PostgreSQL 数据库 dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL 创建 DbContext 上下文 namespace HelloWorld.ProductService.EntityFrameworks { public class ProductServiceDbContext(DbContextOptions<ProductServiceDbContext> options) : DbContext(options) { protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); } } } 创建实体配置类 namespace HelloWorld.ProductService.EntityFrameworks.EntityConfigurations.Products { public class ProductEntityTypeConfiguration : IEntityTypeConfiguration<Product> { public void Configure(EntityTypeBuilder<Product> builder) { builder.ToTable("Products"); builder.Property(x => x.Name).HasMaxLength(32); builder.HasOne(x => x.Brand).WithMany(); } } } 数据库连接字符串 { "ConnectionStrings": { "ProductDatabase": "Host=localhost;Port=5432;Database=ProductService;Username=postgres;Password=postgres" } } 注册数据库上下文 builder.Services.AddDbContext<ProductServiceDbContext>(options => { options.UseNpgsql(builder.Configuration.GetConnectionString(DbConstants.ConnectionStringName)); }); 迁移数据库 dotnet tool install --global dotnet-ef dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add InitialCreate --output-dir EntityFrameworks/Migrations dotnet ef database update 创建模型 namespace HelloWorld.ProductService.Models.Products { public class ProductCreateRequest { public required string Name { get; init; } public string? Description { get; init; } public decimal Price { get; init; } public int BrandId { get; init; } public string? ImageUrl { get; init; } } } 创建自动映射 namespace HelloWorld.ProductService.AutoMapper { public class ProductsMapConfiguration : Profile { public ProductsMapConfiguration() { CreateMap<ProductCreateRequest, Product>(); CreateMap<ProductUpdateRequest, Product>(); CreateMap<Product, ProductListItem>().AfterMap((src, dest) => dest.BrandName = src.Brand.Name); CreateMap<Product, ProductDetailsResponse>(); CreateMap<BrandCreateRequest, Brand>(); CreateMap<BrandUpdateRequest, Brand>(); CreateMap<Brand, BrandDetailsResponse>(); CreateMap<Brand, BrandListItem>(); } } } 创建验证器 namespace HelloWorld.ProductService.Validations.Products { public class BrandUpdateRequestValidator : AbstractValidator<BrandUpdateRequest> { public BrandUpdateRequestValidator() { RuleFor(x => x.Id).GreaterThan(0); RuleFor(x => x.Name).NotNull().NotEmpty().Length(8, 32); } } } 创建控制器 namespace HelloWorld.ProductService.Controllers { [Route("api/[controller]")] [ApiController] public class ProductsController : ControllerBase } 定义权限 namespace HelloWorld.ProductService.PermissionProviders { public static class CatalogPermissions public class CatalogPermissionDefiitionProvider : IPermissionDefinitionProvider } 权限本地化 CatalogPermissionDefinitionProvider.en-US.resx CatalogPermissionDefinitionProvider.zh-CN.resx 实体属性本地化 namespace HelloWorld.ProductService.Entities.Products { public class Product { [Display(Name = "ProductName")] public string Name { get; set; } } } 重复性工作 重复性工作可以通过代码生成工具来减少,例如 Visual Studio 插件, T4 模板, Roslyn 等。

2026年3月10日

实现权限访问控制列表

授权三要素 主体、资源、操作。 例如:用户(主体)对文件(资源)的读写(操作)。主体、资源、操作三者之间的关系称为授权关系,基于这种关系,我们可以将授权关系抽象为一个三元组(主体,资源,操作),这个三元组就是授权的基本单位。 基于角色的访问控制 基于角色的访问控制(Role-Based Access Control,简称 RBAC)是一种访问控制模型,通过角色来控制用户对资源的访问权限。角色是一组权限的集合,用户通过分配角色来获取相应的权限。基于角色的访问控制模型简单易用。还有其它的访问控制模型,如基于属性的访问控制(Attribute-Based Access Control,简称 ABAC)。 权限访问控制列表 权限访问控制列表(Access Control List,简称 ACL)是一种权限控制模型,用于控制用户对资源的访问权限。ACL 通过为每个资源定义一个访问控制列表,来控制用户对资源的操作权限。 ACL 存储设计 public class PermissionGranted { public int Id { get; set; } public int RoleId { get; set; } public required string PermissionName { get; set; } public string? ResourceType { get; set; } public string? ResourceId { get; set; } } 在 DbContext 中配置 ACL 实体 public void Configure(EntityTypeBuilder<PermissionGranted> builder) { builder.ToTable("PermissionGranted"); builder.Property(x => x.Id); builder.Property(x => x.PermissionName).HasMaxLength(64); builder.Property(x => x.ResourceType).HasMaxLength(16); builder.Property(x => x.ResourceId).HasMaxLength(32); builder.HasOne<Role>().WithMany().HasForeignKey(x => x.RoleId).IsRequired(); builder.HasIndex(x => new { x.RoleId, x.PermissionName, x.ResourceType, x.ResourceId }).IsUnique(); } 权限 ACL 存储设计 public class PermissionGranted { public int Id { get; set; } public int RoleId { get; set; } public required string PermissionName { get; set; } public string? ResourceType { get; set; } public string? ResourceId { get; set; } } 在 DbContext 中配置 ACL 实体 public void Configure(EntityTypeBuilder<PermissionGranted> builder) { builder.ToTable("PermissionGranted"); builder.Property(x => x.Id); builder.Property(x => x.PermissionName).HasMaxLength(64); builder.Property(x => x.ResourceType).HasMaxLength(16); builder.Property(x => x.ResourceId).HasMaxLength(32); builder.HasOne<Role>().WithMany().HasForeignKey(x => x.RoleId).IsRequired(); builder.HasIndex(x => new { x.RoleId, x.PermissionName, x.ResourceType, x.ResourceId }).IsUnique(); } 设计一个权限检查器 public interface IPermissionChecker { Task<bool> IsGrantedAsync(string name, string? resourceType = null, string? resourceId = null); Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name, string? resourceType = null, string? resourceId = null); } 实现抽象的权限检查器 遍历每个角色的权限,如果有一个角色拥有该权限,则返回 true,表示授权通过。 ...

2026年3月10日

使用 .NET 迁移助手升级项目

什么是 .NET 升级助手? .NET 升级助手是一个工具,可帮助您将老项目迁移到最新 .NET 框架的工具,它可以帮助您: 识别项目中的依赖项和 API 使用情况。 为项目提供 .NET 目标框架的建议。 提供升级项目所需的代码更改。 在 Visual Studio 中使用 .NET 升级助手 可以使用 Visual Studio 安装 .NET 升级助手扩展。 在命令行中使用 .NET 升级助手 在命令行中使用 .NET 安装升级助手,需要安装 .NET 升级助手 CLI 工具。 dotnet tool install -g upgrade-assistant 将工具迁移工具升级到最新版本 dotnet tool update -g upgrade-assistant 在项目所在的文件夹中运行以下命令: upgrade-assistant --apply-updates 即可升级项目到最新版本。

2026年3月10日

使用 Aspire 启动分布式微服务

准备数据库 docker run --name postgres -e POSTGRES_PASSWORD=postgres -e TZ=Asia/Shanghai -d -p 5432:5432 postgres docker run --name pgadmin -e PGADMIN_DEFAULT_EMAIL=test@test.com -e PGADMIN_DEFAULT_PASSWORD=test -e TZ=Asia/Shanghai -d -p 5050:80 dpage/pgadmin4 初始化 Dapr 运行时 dapr init -s --from-dir C:\dapr 延长等待 RabbitMQ 启动时间 public static IResourceBuilder<IDaprSidecarResource> WithReference(this IResourceBuilder<IDaprSidecarResource> builder, IResourceBuilder<IResourceWithConnectionString> resourceBuilder, int waitInSeconds = 10) 修改 MediatR 依赖注入 之前使用 options.AddBehavior 方法注册行为,现在使用 options.AddOpenBehavior 方法注册行为。 builder.Services.AddMediatR(options => { options.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); options.AddOpenBehavior(typeof(LoggingBehavior<,>)); options.AddOpenBehavior(typeof(ValidatorBehavior<,>)); options.AddOpenBehavior(typeof(TransactionBehavior<,>)); }); 使用分布式事件的微服务需要依赖注入上下文 builder.Services.AddHttpContextAccessor(); Azure Data Studio 删除数据库 -- 终止所有连接到目标数据库的会话 SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'my_database' AND pid <> pg_backend_pid(); -- 删除数据库 DROP DATABASE my_database; IdentityService 演示数据生成前自动创建数据库 await serviceProvider.GetRequiredService<IdentityServiceDbContext>().Database.EnsureCreatedAsync(cancellationToken); 统一使用 DateTimeOffset 类型而不是 DateTime 类型 使用 UTC 时间的优点是可以在不同的时区之间进行转换,而不会丢失时间信息,让程序更好的处理时间,有利于跨时区的应用程序国际化本地化。 ...

2026年3月10日

使用 Azure Data Studio 管理数据库

Azure Data Studio 是一个轻量级的跨平台数据库工具,提供了一个现代化的用户界面,可以帮助用户更轻松地管理数据库。 全部代码开源 Azure Data Studio 是一个开源项目,全部代码都托管在 GitHub 上,用户可以自由查看源代码,也可以参与到项目中来。 https://github.com/Microsoft/azuredatastudio 可管理的数据库 Azure Data Studio 支持多种数据库,包括 SQL Server、PostgreSQL、MySQL 、TimescaleDB、Oracle 等,通过安装相应的扩展,可以支持更多的数据库。 带有智能感知的查询编辑器 丰富的 SQL 编辑器、IntelliSense、关键字完成、代码片段、代码导航和源代码管理集成 (Git))提供一种基于键盘的新式 SQL 编码体验。 智能 SQL 代码片段 Azure Data Studio 提供了大量的 SQL 代码片段,可以帮助用户快速编写 SQL 代码。 使用服务器组 借助服务器组,可以分类管理服务器,方便用户更轻松地管理多个服务器。 使用集成终端 用户界面中的“集成终端”窗口中使用常用的命令行工具,例如:sqlcmd 命令。 使用表设计器 Azure Data Studio 提供了一个表设计器,可以帮助用户更轻松地设计表。 备份和还原数据库 Azure Data Studio 提供了备份和还原数据库的功能,可以帮助用户更轻松地备份和还原数据库。 使用 Copilot 插件 Copilot 插件可以借助强大的 AI 帮助用户更轻松地编写 SQL 代码,提高编码效率。 可使用 Git 管理 SQL 脚本 Azure Data Studio 集成了 Git,可以帮助用户更轻松地管理 SQL 脚本。 ...

2026年3月10日

使用 CQRS 模式实现订单服务

CQRS 是命令和查询责任分离的英文缩写,它是一种将读取操作和更新操作分离的模式,查询返回结果,不改变系统的状态,没有副作用。命令执行更改系统状态,幂等性是指相同的命令多次执行,结果是一样的,不会有副作用。 为什么要使用 CQRS 模式 在传统的体系结构中,使用同一数据模型查询和更新数据库。 这十分简单,非常适用于基本的 CRUD 操作。 CQRS 将读取和写入分离到不同的模型,使用命令来更新数据,使用查询来读取数据。读取存储可以是写入存储的只读副本,或者读取和写入存储可以具有完全不同的结构。 使用多个只读副本可以提高查询性能,尤其是在只读副本靠近应用程序实例的分布式方案中。 使用 MediatR 实现 CQRS 中的 Command 模式 dotnet add package MediatR public class CreateOrderCommand : IRequest<bool> { public string Product { get; set; } public int Quantity { get; set; } } public class CreateOrderCommandHandler1 : IRequestHandler<CreateOrderCommand, bool> { public Task<bool> Handle(CreateOrderCommand request, CancellationToken cancellationToken) { return Task.FromResult(1); } } public class CreateOrderCommandHandler2 : IRequestHandler<CreateOrderCommand, bool> { public Task<bool> Handle(CreateOrderCommand request, CancellationToken cancellationToken) { return Task.FromResult(2); } } public class OrdersController : ControllerBase { private readonly IMediator _mediator; public OrdersController(IMediator mediator) { _mediator = mediator; } [HttpPost] public async Task<IActionResult> CreateOrder(CreateOrderCommand command) { var orderId = await _mediator.Send(command); return Ok(orderId); } } builder.Services.AddMediatR(options => options.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); 使用 Mediator 请求管道处理 CQRS 中的命令 LoggingBehavior、ValidatorBehavior 和 TransactionBehavior ...

2026年3月10日