C#中从数据库获取的多类别项目集合的最优分类实现方案问询
在C#中对数据库获取的多组项目分类的最优方案
嘿,先看看你当前实现里的几个小问题,咱们一步步优化成更优雅、可维护的方案:
当前实现的问题
- 重复代码冗余:
Fruits和Bakery类逻辑几乎完全一致,只是分类ID不同,后续新增分类要重复写大量代码,维护成本很高 - 硬编码&低级错误:
Fruits类里的查询居然用了(int)IdOfCategory.Bakery,这明显是笔误;而且直接把分类ID写在查询里,既不安全也不灵活 - 职责混乱:实体类(比如
Fruits)居然直接承担数据库操作,违反了单一职责原则,类的职责不清晰
最优实现方案
我们可以通过抽象通用逻辑、单一职责拆分和灵活的数据获取策略来优化,具体步骤如下:
1. 定义通用的Product实体类
先把数据库的Product表映射成一个纯数据类,只负责存储产品信息:
public class Product { public int ProductId { get; set; } public string ProductName { get; set; } public string ProductDescription { get; set; } public int CategoryId { get; set; } }
2. 规范分类枚举(保持语义清晰)
调整枚举命名更符合C#规范,后续新增分类直接加在这里就行:
public enum CategoryId { Fruits = 1, Bakery = 2 }
3. 抽象数据访问层(Repository模式)
把所有数据库操作集中到一个类里,避免重复代码,同时使用参数化查询防止SQL注入:
public class ProductRepository { // 把连接字符串抽出来,方便后续配置(比如移到appsettings.json) private readonly string _connectionString = @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\user\Documents\mario.mdf;Integrated Security=True;Connect Timeout=30"; /// <summary> /// 根据分类ID获取对应产品(适合只需要单个分类数据的场景) /// </summary> public List<Product> GetProductsByCategory(CategoryId categoryId) { var products = new List<Product>(); // 参数化查询,避免SQL注入,同时更灵活 string query = @"SELECT ProductId, ProductName, ProductDescription, CategoryId FROM Products WHERE CategoryId = @CategoryId"; using (SqlConnection connection = new SqlConnection(_connectionString)) using (SqlCommand command = new SqlCommand(query, connection)) { command.Parameters.AddWithValue("@CategoryId", (int)categoryId); connection.Open(); using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { products.Add(MapReaderToProduct(reader)); } } } return products; } /// <summary> /// 一次性获取所有产品并按分类分组(适合需要多个分类数据的场景,减少数据库连接次数) /// </summary> public Dictionary<CategoryId, List<Product>> GetAllProductsGroupedByCategory() { var allProducts = new List<Product>(); string query = @"SELECT ProductId, ProductName, ProductDescription, CategoryId FROM Products"; using (SqlConnection connection = new SqlConnection(_connectionString)) using (SqlCommand command = new SqlCommand(query, connection)) { connection.Open(); using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { allProducts.Add(MapReaderToProduct(reader)); } } } // 用LINQ快速完成分组,代码简洁高效 return allProducts.GroupBy(p => (CategoryId)p.CategoryId) .ToDictionary(group => group.Key, group => group.ToList()); } // 抽取出Reader到Product的映射逻辑,避免重复代码 private Product MapReaderToProduct(SqlDataReader reader) { return new Product { ProductId = reader.GetInt32(0), ProductName = reader.GetString(1), // 处理可能的空值,避免抛出异常 ProductDescription = reader.IsDBNull(2) ? null : reader.GetString(2), CategoryId = reader.GetInt32(3) }; } }
4. 使用示例
现在你可以根据需求选择不同的获取方式:
// 场景1:只需要某一个分类的产品 var productRepo = new ProductRepository(); var fruits = productRepo.GetProductsByCategory(CategoryId.Fruits); var bakeryItems = productRepo.GetProductsByCategory(CategoryId.Bakery); // 场景2:需要多个分类的产品,一次性获取再分组更高效 var groupedProducts = productRepo.GetAllProductsGroupedByCategory(); var fruitsFromGroup = groupedProducts[CategoryId.Fruits]; var bakeryFromGroup = groupedProducts[CategoryId.Bakery];
优化后的优势
- 单一职责:数据操作集中在Repository,实体类只负责存储数据,代码结构清晰
- 可维护性:新增分类只需要在枚举里加值,不用写新的类和重复的查询逻辑
- 安全性:参数化查询彻底避免SQL注入风险
- 灵活性:两种获取策略可选,根据数据量和需求选择最优方式
- 可读性:代码结构清晰,命名规范,后续接手的人能快速理解
内容的提问来源于stack exchange,提问作者Marky
相关产品推荐
相关产品推荐

