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
英文地址,請注明出處:http://en.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,