c#_数据持久化


数据持久化架构

数据是应用程序的命脉。持久化架构的选择直接决定了应用的性能、可扩展性、复杂度和维护成本。本章将深入探讨.NET生态中主流的数据访问模式、工具和策略,帮助你为你的系统做出最明智的数据决策。


5.1 ORM之争:Entity Framework Core深度剖析

对象关系映射(ORM)是一种通过将数据库中的关系数据与应用程序中的对象模型进行相互转换的技术。它旨在解决所谓的“阻抗不匹配”问题,让开发者能够以操作对象的方式来操作数据库,从而极大提升开发效率。

在.NET领域,Entity Framework Core (EF Core) 是微软官方推出、功能强大且应用最广泛的ORM。它远不止是一个数据访问层(DAL)库,更是一个完整的持久化架构解决方案。

5.1.1 Code First 与数据库迁移(Migrations)

EF Core 强烈推荐并支持 Code First 开发模式。你首先在代码中定义领域模型(实体类),然后由EF Core根据模型来生成或更新数据库 schema。

1. 定义实体和上下文(DbContext):

// 领域实体
public class Blog {public int BlogId { get; set; } // 主键约定:名为Id或[Class]Id的属性会被认作主键public string Url { get; set; }// 导航属性 (Navigation Property):定义对象间的关系public virtual List<Post> Posts { get; set; } // 一个Blog有零个或多个Post (一对多)
}public class Post {public int PostId { get; set; }public string Title { get; set; }public string Content { get; set; }public int BlogId { get; set; } // 外键public virtual Blog Blog { get; set; } // 反向导航属性 (多对一)
}// 数据库上下文:代表与数据库的会话,是核心类
public class BloggingContext : DbContext {public BloggingContext(DbContextOptions<BloggingContext> options) : base(options) { }// DbSet<T> 属性代表数据库中的表public DbSet<Blog> Blogs { get; set; }public DbSet<Post> Posts { get; set; }// (可选) 使用Fluent API进行更精细的模型配置protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.Entity<Blog>(entity => {entity.Property(b => b.Url).IsRequired().HasMaxLength(500); // 配置属性entity.HasIndex(b => b.Url).IsUnique(); // 创建唯一索引});}
}

2. 注册DbContext(通常在Program.cs中):

// 使用SQL Server,并注册为Scoped生命周期(非常重要!)
builder.Services.AddDbContext<BloggingContext>(options =>options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

3. 数据库迁移(Migrations):
迁移是EF Core用于管理数据库 schema 演化的核心工具。它将模型更改同步到数据库,并保留历史记录,使得团队协作和部署变得安全可靠。

# 在Package Manager Console中或使用.NET CLI创建迁移
Add-Migration InitialCreate # PMC
# 或者
dotnet ef migrations add InitialCreate# 将迁移应用到数据库(创建或更新表结构)
Update-Database # PMC
# 或者
dotnet ef database update

架构师视角:

  • Code First的优势:使领域模型成为系统设计的核心,数据库 schema 是其副产品。这更符合DDD(领域驱动设计)的理念。
  • 迁移的价值:迁移文件是代码,可以纳入版本控制(如Git)。这实现了数据库 schema 的版本化可重复的部署过程,是DevOps实践的关键一环。
  • 谨慎使用自动迁移:在生产环境中,应避免使用自动迁移(context.Database.Migrate())。应在CI/CD管道中通过CLI命令可控地执行数据库更新,并始终先在预发布环境进行测试。
5.1.2 LINQ高效查询与性能优化

EF Core 将 LINQ (Language Integrated Query) 查询转换为SQL语句,这是其强大功能的核心。

基本查询:

// 简单的LINQ查询,EF Core会将其转换为 SQL: SELECT * FROM Blogs WHERE Url = @url
var blog = await _context.Blogs.FirstOrDefaultAsync(b => b.Url == "https://example.com");// 包含关联数据的查询 (Eager Loading)
var blogsWithPosts = await _context.Blogs.Include(b => b.Posts) // SQL JOIN.Where(b => b.Posts.Any()).ToListAsync();// 投影查询 (Projection) - 只选择需要的字段,更高效
var blogTitles = await _context.Blogs.Where(b => b.Url.Contains("dotnet")).Select(b => new { b.BlogId, b.Url }) // 不SELECT *,只取特定列.ToListAsync();

性能优化策略:

  1. 避免SELECT N+1问题:这是ORM最常见的性能陷阱。

    // ❌ 错误示例:N+1查询
    var blogs = await _context.Blogs.ToListAsync();
    foreach (var blog in blogs) {// 每次循环都会对数据库执行一次查询来获取Posts!var posts = await _context.Entry(blog).Collection(b => b.Posts).Query().ToListAsync();
    }// ✅ 正确示例:使用Include或投影一次性加载
    var blogsWithPosts = await _context.Blogs.Include(b => b.Posts).ToListAsync();
    
  2. 使用异步方法:始终使用 ToListAsync(), FirstOrDefaultAsync(), SaveChangesAsync() 等异步方法,避免阻塞线程,提高应用程序的并发扩展能力。

  3. 全局查询过滤器(Global Query Filters):在 OnModelCreating 中定义,自动应用于所有相关查询。常用于“软删除”(IsDeleted 标志)或多租户数据隔离。

    protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.Entity<Blog>().HasQueryFilter(b => !b.IsDeleted);
    }
    // 任何查询_context.Blogs的操作,都会自动附加WHERE IsDeleted = false条件
    
  4. 显式编译查询(Explicitly Compiled Queries):对于在热点路径上执行的确切查询,编译一次并缓存结果,可以带来小幅性能提升。

    private static readonly Func<BloggingContext, string, Task<Blog>> _blogByUrl =EF.CompileAsyncQuery((BloggingContext context, string url) =>context.Blogs.FirstOrDefault(b => b.Url == url));// 使用
    var blog = await _blogByUrl(_context, "https://example.com");
    
5.1.3 变更跟踪、并发冲突处理

变更跟踪(Change Tracking):
DbContext 会自动跟踪从它加载的实体的状态。当你修改实体属性后,调用 SaveChangesAsync(),EF Core 会自动生成相应的 INSERT, UPDATE, DELETE 语句。

var blog = await _context.Blogs.FindAsync(1);
blog.Url = "https://new-url.com"; // EF Core检测到这项更改
await _context.SaveChangesAsync(); // 生成并执行 SQL: UPDATE Blogs SET Url = ... WHERE BlogId = 1

并发冲突(Concurrency Conflicts):
当多个用户尝试同时更新同一条记录时,可能会发生并发冲突。EF Core 使用乐观并发机制来处理。

  1. 配置并发令牌(Concurrency Token)

    protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.Entity<Blog>().Property(b => b.Timestamp) // 可以是一个Timestamp/RowVersion列.IsRowVersion() // 在SQL Server中配置为rowversion类型.IsConcurrencyToken(); // 标记为并发令牌
    }
    
  2. 处理 DbUpdateConcurrencyException

    try {await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException ex) {// 1. 获取未能保存的实体var entry = ex.Entries.Single();// 2. 获取数据库中的当前值var databaseValues = await entry.GetDatabaseValuesAsync();if (databaseValues == null) {// 记录已被删除} else {// 记录已被其他用户修改// 3. 决定如何解决冲突:使用客户端值、数据库值或合并// 例如:重新显示编辑界面,让用户决定entry.OriginalValues.SetValues(databaseValues); // 刷新原始值,重试// 或者:自定义合并逻辑...}
    }
    

架构师视角:
EF Core 是一个功能极其丰富的工具。它的优势在于开发效率对领域模型的专注度。然而,它并非银弹。

  • 适用场景

    • 业务逻辑复杂的应用程序(CRUD及其延伸)。
    • 需要快速迭代和频繁进行数据库 schema 更改的项目。
    • 开发团队希望专注于对象模型而非SQL细节。
  • 潜在缺点

    • 性能:复杂的LINQ查询可能生成低效的SQL,需要开发者具备一定的SQL知识来检查和优化。
    • 黑盒魔法:过度依赖其自动化功能可能导致开发者对底层数据库操作失去理解和控制。
    • 批量操作支持:虽然EF Core 7+改进了批量操作,但大规模批量更新/删除仍不如原生SQL高效。

5.2 仓储模式(Repository)与工作单元模式(Unit of Work)的实现与争议

仓储和工作单元(UoW)模式是领域驱动设计(DDD)中的核心模式,旨在为领域模型提供一个抽象的持久化接口,并将数据访问细节与业务逻辑解耦。在早期,它们是应对笨重ORM(如EF4)和复杂数据访问层的必要手段。然而,在现代,尤其是与EF Core这样的ORM一起使用时,其必要性和实现方式引发了广泛讨论。

5.2.1 经典实现

让我们先看看这两种模式的经典定义和实现。

1. 仓储模式 (Repository Pattern)

  • 意图:在领域层和数据映射层之间提供一个类似集合的接口,用于访问领域对象。客户端代码通过抽象接口与仓储交互,完全不知道数据如何持久化。
  • 通用仓储接口示例
    // 在领域层或核心层定义的接口
    public interface IRepository<T> where T : class, IAggregateRoot {Task<T?> GetByIdAsync(int id);Task<IEnumerable<T>> GetAllAsync();Task AddAsync(T entity);void Update(T entity);void Remove(T entity);// ... 可能包含其他通用方法,如FindByCondition
    }// 特定实体的仓储接口可以扩展通用接口,添加特定查询方法
    public interface IProductRepository : IRepository<Product> {Task<IEnumerable<Product>> GetProductsByCategoryAsync(string category);Task<Product?> GetProductWithDetailsAsync(int productId);
    }
    

2. 工作单元模式 (Unit of Work Pattern)

  • 意图:维护一个受业务事务影响的对象列表,并协调变化的写入和并发问题的解决。它的核心是保证一系列操作要么全部成功,要么全部失败,并保持一致性。
  • 接口示例
    public interface IUnitOfWork : IDisposable {IProductRepository Products { get; }IOrderRepository Orders { get; }// ... 其他仓储Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default);
    }
    

3. 基于EF Core的实现

// 通用仓储实现
public class EfRepository<T> : IRepository<T> where T : class {protected readonly DbContext _context;protected readonly DbSet<T> _dbSet;public EfRepository(DbContext context) {_context = context;_dbSet = context.Set<T>();}public virtual async Task<T?> GetByIdAsync(int id) => await _dbSet.FindAsync(id);public virtual async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();public virtual async Task AddAsync(T entity) => await _dbSet.AddAsync(entity);public virtual void Update(T entity) => _dbSet.Update(entity);public virtual void Remove(T entity) => _dbSet.Remove(entity);
}// 特定仓储实现
public class ProductRepository : EfRepository<Product>, IProductRepository {public ProductRepository(MyDbContext context) : base(context) { }public async Task<IEnumerable<Product>> GetProductsByCategoryAsync(string category) {return await _dbSet.Where(p => p.Category == category).ToListAsync();}// ... 其他特定实现
}// 工作单元实现
public class UnitOfWork : IUnitOfWork {private readonly MyDbContext _context;public IProductRepository Products { get; }public IOrderRepository Orders { get; }public UnitOfWork(MyDbContext context) {_context = context;Products = new ProductRepository(context);Orders = new OrderRepository(context);}public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) {return await _context.SaveChangesAsync(cancellationToken);}// ... Dispose 实现等
}// 在业务逻辑层(应用层)中的使用
public class ProductService {private readonly IUnitOfWork _unitOfWork;public ProductService(IUnitOfWork unitOfWork) {_unitOfWork = unitOfWork;}public async Task UpdateProductPrice(int productId, decimal newPrice) {var product = await _unitOfWork.Products.GetByIdAsync(productId);if (product != null) {product.Price = newPrice;// _unitOfWork.Products.Update(product); // 通常EF Core变更跟踪不需要显式调用Updateawait _unitOfWork.SaveChangesAsync();}}
}
5.2.2 争议与现代化审视

上述经典实现曾是许多项目的标准,但在EF Core的背景下,其价值受到了挑战。

1. 争议点:抽象泄漏与过度封装

  • IQueryable<T> 的困境:通用仓储的一个巨大问题是是否暴露 IQueryable<T>
    • 暴露它:允许在业务层进行灵活的LINQ组合,但这破坏了抽象。调用者实际上是在构建表达式树,这些树最终会被转换为SQL,这意味着他们仍然需要了解数据库 schema 的细节。这被称为“抽象泄漏”(Leaky Abstraction)。
    • 不暴露它:为了保持纯粹的抽象,仓储接口只能返回 IEnumerable<T>List<T>。但这会导致性能问题(例如,GetAll().Where(...).ToList() 会在内存中过滤,而不是在数据库中)和功能缺失(无法实现分页等操作,除非在接口中定义大量特定方法)。

2. 争议点:EF Core本身已经实现了这些模式

  • DbSet<T> 就是一个仓储DbSet<T> 提供了集合式的接口(Add, Remove, Find, Where)。
  • DbContext 就是一个工作单元:它跟踪所有更改的实体,并通过 SaveChangesAsync() 以原子方式持久化所有更改。
    • 因此,再包裹一层 IRepository<T>IUnitOfWork,很多时候只是在委托调用底层的 DbSetDbContext,增加了大量的样板代码,却没有提供任何实际价值,反而增加了复杂性。

3. 对测试的价值减弱:过去,这些模式的一个重要目的是为了可测试性,以便可以用Mock仓储来模拟数据库。然而:
* 使用EF Core的内存数据库提供程序Microsoft.EntityFrameworkCore.InMemory)可以更直接地进行集成测试,效果往往比Mock更好,因为它测试的是真实的查询逻辑。
* Mock DbSetDbContext 非常复杂且脆弱,Mock一个简单的 IRepository 接口反而更容易,但这仍然是测试实现细节而非行为。

5.2.3 现代实践与建议

那么,在现代应用程序中,我们应该如何对待这些模式呢?架构师需要根据上下文做出权衡。

方案A:完全放弃通用仓储,直接使用DbContext
对于许多应用,这是最简单、最直接的方式。

  • 优点:代码量最少,没有不必要的抽象,性能最佳(无需额外委托调用),充分利用EF Core的全部功能。
  • 缺点:业务层直接依赖EF Core,理论上的耦合度更高。
  • 适用场景:中小型应用、CRUD为主的系统、团队熟悉EF Core且不计划切换数据访问技术。
// 直接在应用服务中使用DbContext
public class ProductService {private readonly MyDbContext _context; // 直接依赖DbContextpublic ProductService(MyDbContext context) {_context = context;}public async Task<List<ProductDto>> GetProductsByCategory(string category) {// 直接使用LINQ,强大而灵活return await _context.Products.Where(p => p.Category == category && p.IsActive).OrderBy(p => p.Name).Select(p => new ProductDto { Id = p.Id, Name = p.Name, Price = p.Price }) // 投影查询,高效.ToListAsync();}
}

方案B:为特定聚合根定义特定仓储接口
这是DDD纯化论者和大型复杂系统的推荐做法。

  • 核心思想不要为每个实体都创建一个仓储。只为聚合根(Aggregate Root) 创建仓储。聚合根内的子实体通过根进行访问。
  • 接口设计:接口不是通用的 IRepository<T>,而是定义明确的、基于领域语言的特定方法。它位于领域层,由其实现(在基础设施层)决定如何使用EF Core。
  • 优点:提供了真正有意义的持久化抽象,接口反映领域概念而非数据访问细节,非常适合复杂领域逻辑。
  • 缺点:需要更多代码,需要更深入的DDD知识。
// 在领域层
public interface IOrderRepository {// 领域特定方法,表达业务意图Task<Order?> GetByIdAsync(OrderId orderId);Task<Order?> GetDraftOrderByUserIdAsync(UserId userId); // 查找用户的草稿订单Task<IEnumerable<Order>> GetShippedOrdersInDateRangeAsync(DateTime start, DateTime end);void Add(Order order);// 没有通用的Update,变更通过聚合根内部状态变化,由UoW跟踪
}// 在基础设施层(数据访问层)
public class OrderRepository : IOrderRepository {private readonly MyDbContext _context;public OrderRepository(MyDbContext context) => _context = context;public async Task<Order?> GetByIdAsync(OrderId orderId) {// 显式地包含(Include)所有需要加载的子实体return await _context.Orders.Include(o => o.LineItems).Include(o => o.ShippingAddress).FirstOrDefaultAsync(o => o.Id == orderId);}// ... 实现其他特定方法
}// 在应用层使用
public class CreateOrderService {private readonly IOrderRepository _orderRepository;// 不再需要通用的IUnitOfWork,因为DbContext本身就是UoWprivate readonly MyDbContext _context;public CreateOrderService(IOrderRepository orderRepository, MyDbContext context) {_orderRepository = orderRepository;_context = context; // 同时注入DbContext用于最终保存}public async Task ExecuteAsync(CreateOrderCommand command) {var draftOrder = await _orderRepository.GetDraftOrderByUserIdAsync(command.UserId);if (draftOrder != null) {draftOrder.UpdateLineItem(command.ProductId, command.Quantity);} else {draftOrder = new Order(command.UserId);draftOrder.AddLineItem(command.ProductId, command.Quantity);_orderRepository.Add(draftOrder);}// 业务逻辑完成后,调用DbContext的SaveChangesawait _context.SaveChangesAsync();}
}

方案C:使用MediatR和垂直切片架构彻底规避争议
这是一种更激进的现代化方法。它将关注点从“层次”(数据访问层、服务层)转移到“功能切片”(每个命令/查询都是一个独立的切片)。

  • 每个命令(Command)或查询(Query)处理器自己负责其数据访问。
  • 它可以直接使用 DbContext,也可以为非常复杂的查询定义一个专门的“查询器”(Query Service)。
  • 这完全避免了是否需要仓储的争论,因为每个用例都是独立的。
public class GetProductsByCategoryQuery : IRequest<List<ProductDto>> {public string Category { get; set; }
}public class GetProductsByCategoryHandler : IRequestHandler<GetProductsByCategoryQuery, List<ProductDto>> {private readonly MyDbContext _context; // 直接使用DbContextpublic GetProductsByCategoryHandler(MyDbContext context) => _context = context;public async Task<List<ProductDto>> Handle(GetProductsByCategoryQuery request, CancellationToken ct) {return await _context.Products.Where(p => p.Category == request.Category).Select(p => new ProductDto { ... }).ToListAsync(ct);}
}
5.2.4 架构师决策指南
方案适用场景优点缺点
A: 直接使用 DbContext中小型应用,CRUD为主,团队效率优先简单、直接、灵活、代码少业务层与EF Core耦合
B: 特定聚合仓储大型复杂领域,遵循DDD,需要清晰边界高可测试性,持久化细节完全隐藏,领域纯粹代码量大,设计更复杂
C: 垂直切片追求极致简洁和灵活性的应用无争议,功能高度内聚,依赖清晰可能在不同处理器中出现重复查询逻辑

建议

  1. 从方案A开始:除非你有明确的、令人信服的理由(如极其复杂的领域),否则优先选择直接使用 DbContext。它能在大多数场景下提供最佳的生产力。
  2. 谨慎引入方案B:只在识别出真正的聚合根,并且其数据访问逻辑确实复杂且需要隐藏时,才为其创建特定的仓储接口。避免创建“贫血”的通用仓储。
  3. 记住目的:模式是手段,不是目的。使用它们的目的是为了降低复杂度解耦。如果它们反而增加了复杂度,那就值得重新审视。
  4. 统一团队共识:在项目初期就团队应采用哪种数据访问模式达成一致,并形成规范,这比选择哪种具体方案更重要。

总结
仓储和工作单元模式并非过时,但其应用方式需要根据现代ORM的能力进行重新审视。盲目套用传统的通用实现已成为一种“反模式”。

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

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

相关文章

996引擎-骰子功能

996引擎-骰子功能 测试NPC QF回调函数 结果 参考资料 在测试NPC播放骰子动画。 播放前需要先设置骰子点数 测试NPC [[骰子的显示顺序和点数 对应 私人变量 D0 D1 D2 D3 D4 D5]] -- NPC入口函数 function main(player)-- 骰子共6个,设置骰子点数后,再执行摇骰子,否则没动画…

Vue 3多语言应用开发实战:vue-i18n深度解析与最佳实践

&#x1f4d6; 概述 Vue 3 国际化&#xff08;i18n&#xff09;是构建多语言应用的核心需求。本文档介绍 Vue 3 中实现国际化的主流方案&#xff0c;包括 vue-i18n、Vite 插件方案和自定义解决方案。 &#x1f3af; 主流方案对比 方案优点缺点适用场景vue-i18n功能完整、生态成…

港口船舶流量统计准确率↑27%!陌讯多模态融合算法实战解析

一、行业痛点&#xff1a;港口船舶流量统计的三大核心难题智慧港口建设中&#xff0c;船舶流量统计是泊位调度、航道管理与安全预警的核心数据支撑&#xff0c;但传统方案受场景特性限制&#xff0c;长期存在难以解决的技术瓶颈。据《2023 年中国港口智能化发展报告》显示&…

Shell脚本的基础知识学习

Shell 脚本是 Linux/Unix 系统的核心自动化工具&#xff0c;能够完成以下任务&#xff1a; &#xff08;1&#xff09;批量操作&#xff1a;一键安装软件、批量处理文件&#xff08;重命名、压缩、备份等&#xff09;。 &#xff08;2&#xff09;系统管理&#xff1a;监控资源…

k8s部署,pod管理,控制器,微服务,集群储存,集群网络及调度,集群认证

k8s部署 k8s中容器的管理方式 ​ Kubernetes集群创建方式 centainerd 默认情况下&#xff0c;K8S在创建集群时使用的方式 docker docker使用的普记录最高&#xff0c;虽然K8S在1.24版本后已经费力了kubelet对docker的支持&#xff0c;但时可以借助cri-docker方式来实现集…

JAVA限流方法

在 Java 项目中限制短时间内的频繁访问&#xff08;即接口限流&#xff09;&#xff0c;是保护系统资源、防止恶意攻击或高频请求导致过载的重要手段。常见实现方案可分为单机限流和分布式限流&#xff0c;以下是具体实现方式&#xff1a;一、核心限流算法无论哪种方案&#xf…

性能比拼: .NET (C#) vs. Fiber (Go)

本内容是对知名性能评测博主 Anton Putra .NET (C#) vs. Fiber (Go): Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 在本视频中&#xff0c;我们将对比 C# 与 .NET 框架和 Golang 的表现。在第一个…

信誉代币的发行和管理机制是怎样的?

信誉代币的发行与管理机制是区块链技术与经济模型深度融合的产物&#xff0c;其核心在于通过代码和社区共识构建可量化、可验证的信任体系。以下从技术架构、经济模型、治理机制三个维度展开分析&#xff0c;并结合具体案例说明&#xff1a;一、发行机制&#xff1a;行为即价值…

神经网络|(十二)概率论基础知识-先验/后验/似然概率基本概念

【1】引言 前序学习进程中&#xff0c;对贝叶斯公式曾经有相当粗糙的回归&#xff0c;实际上如果我们看教科书或者网页&#xff0c;在讲贝叶斯公式的时候&#xff0c;会有几个名词反复轰炸&#xff1a;先验概率、后验概率、似然概率。 今天就来把它们解读一下&#xff0c;为以…

使用UE5开发《红色警戒3》类战略养成游戏的硬件配置指南

从零开始&#xff0c;学习 虚幻引擎5&#xff08;UE5&#xff09;&#xff0c;开始游戏开发之旅&#xff01;本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01;开发类似《红色警戒3》级别的战略养成游戏&#xff0c;其硬件需求远超普通2D或小型3D项目——这类游戏…

Vue2+Vue3前端开发_Day12-Day14_大事件管理系统

参考课程: 【黑马程序员 Vue2Vue3基础入门到实战项目】 [https://www.bilibili.com/video/BV1HV4y1a7n4] ZZHow(ZZHow1024) 项目收获 Vue3 composition APIPinia / Pinia 持久化处理Element Plus&#xff08;表单校验&#xff0c;表格处理&#xff0c;组件封装&#xff09…

[ACTF新生赛2020]明文攻击

BUUCTF在线评测BUUCTF 是一个 CTF 竞赛和训练平台&#xff0c;为各位 CTF 选手提供真实赛题在线复现等服务。https://buuoj.cn/challenges#[ACTF%E6%96%B0%E7%94%9F%E8%B5%9B2020]%E6%98%8E%E6%96%87%E6%94%BB%E5%87%BB下载查看&#xff0c;一个压缩包和一张图片。压缩包需要密…

关于日本服务器的三种线路讲解

租用日本服务器时&#xff0c;哪种线路选择更适合?当初次接触跨境业务的站长们着手租用日本服务器时&#xff0c;会发现不同服务商提供的网络线路五花八门&#xff0c;从陌生的运营商名称到复杂的技术参数&#xff0c;常常使其感到眼花缭乱。为了帮助大家理清思路&#xff0c;…

【大白话解析】 OpenZeppelin 的 MerkleProof 库:Solidity 默克尔证明验证工具全指南​​(附源代码)

🧩 一、Merkle Tree 是什么?为什么要验证它? 想象你有一个名单,比如: ["Alice", "Bob", "Charlie", "Dave"] 你想让别人验证:“我(比如 Alice)是不是在这个名单里?”,但不想把整个名单都放在区块链上(太贵!)。 于是你…

机械学习综合练习项目

数据集合完整项目文件已经上传一、项目介绍案例介绍 案例是针对“红酒.csv”数据集&#xff0c;在红葡萄酒质量分析的场景 中&#xff0c;利用多元线性回归来探索红葡萄酒的不同化学成分如何共同 影响其质量评分。在建立线性回归模型之后&#xff0c;当给出了红葡萄酒 的新的一…

第3篇:配置管理的艺术 - 让框架更灵活

前言 在前一章中&#xff0c;我们设计了强大的注解API。本章将深入探讨配置管理系统的设计&#xff0c;学习如何将注解中的声明式配置转换为运行时可用的配置对象。 配置管理的核心挑战 在我们的框架中&#xff0c;配置来源有三个层级&#xff1a;主要挑战&#xff1a; &#x…

发版混乱怎么规范

你是否经历过这种场景&#xff1a;临到发版&#xff0c;一堆功能代码挤在一起&#xff0c;测试分不清范围&#xff0c;修复一个Bug可能引发三个新Bug&#xff1f;发布过程像一场豪赌&#xff1f;问题的核心往往在于分支策略和流程的混乱。今天&#xff0c;我们就来建立一套在绝…

【golang长途旅行第30站】channel管道------解决线程竞争的好手

channel 为什么需要channel 使用全局变量加锁同步来解决goroutine的竞争&#xff0c;可以但不完美难以精确控制等待时间​&#xff08;主线程无法准确知道所有 goroutine 何时完成&#xff09;。全局变量容易引发竞态条件​&#xff08;即使加锁&#xff0c;代码复杂度也会增加…

苹果XR芯片介绍

苹果的 XR 芯片技术主要体现在 A 系列、M 系列处理器以及专为空间计算设计的 R1 协处理器中。以下从技术架构、产品迭代和综合对比三个维度展开分析&#xff1a;一、技术架构解析1. A 系列芯片&#xff08;以 A12 Bionic 为例&#xff09;制程工艺&#xff1a;7nm&#xff08;台…

达梦数据库巡检常用SQL(三)

达梦数据库巡检常用SQL(三) 数据库SQL运行检查 数据库SQL运行检查 死锁的事务情况: SELECT TO_CHAR(HAPPEN_TIME,YYYY-MM-DD HH24:MI:SS) HAPPEN_TIME,SQL_TEXT FROM V$DEADLOCK_HISTORY WHERE HAPPEN_TIME >DATEADD(DAY,-30,