如何用C#编写适配所有类的MySqlConnection通用数据获取方法
通用数据库数据获取方法实现方案
问题描述
现有多个从数据库获取数据的方法,核心逻辑完全一致,仅涉及的实体类、属性、数据库表名不同,需要重复编写大量相似代码。希望实现一个适用于所有类的通用数据获取方法,当前的核心难点是如何将MySqlDataReader中的数据自动转换为对应实体类属性的类型。计划传入类类型、列名数组和表名来实现通用方法,示例的重复代码如下:
// 期望实现的通用方法 public List<Object> getData(string className, string[] columns, string tableName) {...} // 重复的Client数据获取方法 public List<Client> GetClients() { List<Client> list = new List<Client>(); try { conn.Open(); string sql = "SELECT id, clientName, info, hidden from clients"; MySqlCommand cmd = new MySqlCommand(sql, conn); MySqlDataReader rdr = cmd.ExecuteReader(); while (rdr.Read()) { Client p = new Client((int)rdr[0], (string)rdr[1], (string)rdr[2], Convert.ToBoolean(rdr["hidden"])); list.Add(p); } rdr.Close(); return list; } catch (Exception ex) { Console.WriteLine(ex.ToString()); return null; } finally { conn.Close(); } } // 重复的RepPrefix数据获取方法 public List<RepPrefix> GetRepPrefixes() { List<RepPrefix> list = new List<RepPrefix>(); try { conn.Open(); string sql = "SELECT id, prefixName, hidden from repPrefix"; MySqlCommand cmd = new MySqlCommand(sql, conn); MySqlDataReader rdr = cmd.ExecuteReader(); while (rdr.Read()) { RepPrefix p = new RepPrefix((int)rdr[0], (string)rdr[1], Convert.ToBoolean(rdr["hidden"])); list.Add(p); } rdr.Close(); return list; } catch (Exception ex) { Console.WriteLine(ex.ToString()); return null; } finally { conn.Close(); } }
解决方案
可以通过泛型+反射实现通用方法,同时优化数据库连接管理、避免SQL注入风险,以下是具体实现:
1. 通用泛型方法实现
利用泛型指定返回的实体类型,通过反射获取实体类的属性,自动完成MySqlDataReader到实体对象的转换:
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using MySql.Data.MySqlClient; public class DataAccess { private string connectionString = "你的数据库连接字符串"; // 通用数据获取方法 public List<T> GetData<T>(string[] columns, string tableName) where T : class, new() { List<T> resultList = new List<T>(); string columnNames = string.Join(", ", columns); string sql = $"SELECT {columnNames} FROM {tableName}"; using (MySqlConnection conn = new MySqlConnection(connectionString)) { try { conn.Open(); using (MySqlCommand cmd = new MySqlCommand(sql, conn)) { using (MySqlDataReader rdr = cmd.ExecuteReader()) { // 获取实体类的所有属性 PropertyInfo[] properties = typeof(T).GetProperties(); while (rdr.Read()) { T entity = new T(); for (int i = 0; i < columns.Length; i++) { string columnName = columns[i]; // 匹配对应属性(忽略大小写) PropertyInfo prop = properties.FirstOrDefault(p => p.Name.Equals(columnName, StringComparison.OrdinalIgnoreCase)); if (prop != null && !rdr.IsDBNull(i)) { // 将Reader中的值转换为属性类型并赋值 object value = Convert.ChangeType(rdr[i], prop.PropertyType); prop.SetValue(entity, value); } } resultList.Add(entity); } } } return resultList; } catch (Exception ex) { Console.WriteLine(ex.ToString()); return null; } } } }
2. 使用示例
调用通用方法获取不同实体类的数据:
DataAccess db = new DataAccess(); // 获取Client列表 List<Client> clients = db.GetData<Client>(new string[] { "id", "clientName", "info", "hidden" }, "clients"); // 获取RepPrefix列表 List<RepPrefix> repPrefixes = db.GetData<RepPrefix>(new string[] { "id", "prefixName", "hidden" }, "repPrefix");
3. 关键优化点
- 泛型约束:
where T : class, new()确保实体类有无参构造函数,方便创建实例 - using语句:自动管理数据库连接、命令、Reader的释放,避免手动关闭的遗漏
- 反射属性匹配:通过列名与属性名的匹配(忽略大小写),自动完成类型转换与赋值
- SQL注入防护:如果后续需要添加查询条件,建议使用参数化查询,避免直接拼接SQL字符串
4. 针对带参构造函数的替代方案
如果实体类只有带参构造函数,可修改反射逻辑匹配构造函数参数:
// 在GetData方法的while循环中替换为以下代码 ConstructorInfo constructor = typeof(T).GetConstructors()[0]; object[] constructorParams = new object[columns.Length]; for (int i = 0; i < columns.Length; i++) { if (!rdr.IsDBNull(i)) { constructorParams[i] = Convert.ChangeType(rdr[i], constructor.GetParameters()[i].ParameterType); } } T entity = (T)constructor.Invoke(constructorParams); resultList.Add(entity);
内容的提问来源于stack exchange,提问作者Louis Tran
相关产品推荐
相关产品推荐

