C#中IRepository接口如何实现多类型多数量键的灵活查询方法?
针对ADO.NET的灵活多键检索Repository方案
方案1:使用匿名类型传递键值
这种方式最简洁,无需额外定义类型,直接用匿名对象传入对应键名和值。
接口修改:
public interface IRepository<T> where T : class { // 原有方法... Task<T?> GetByKeyAsync(object keyObject); }
ADO.NET实现示例:
public async Task<T?> GetByKeyAsync(object keyObject) { var properties = keyObject.GetType().GetProperties(); if (!properties.Any()) return null; var whereClauses = new List<string>(); var parameters = new List<SqlParameter>(); // 生成参数化WHERE条件,避免SQL注入 foreach (var prop in properties) { var value = prop.GetValue(keyObject); if (value == null) continue; var paramName = $"@{prop.Name}"; whereClauses.Add($"{prop.Name} = {paramName}"); parameters.Add(new SqlParameter(paramName, value)); } var sql = $"SELECT * FROM {typeof(T).Name} WHERE {string.Join(" AND ", whereClauses)}"; using var conn = new SqlConnection(_connectionString); await conn.OpenAsync(); using var cmd = new SqlCommand(sql, conn); cmd.Parameters.AddRange(parameters.ToArray()); using var reader = await cmd.ExecuteReaderAsync(); if (await reader.ReadAsync()) { // 实体映射逻辑(可根据需求替换为自定义映射或轻量ORM) var entity = Activator.CreateInstance<T>(); foreach (var prop in typeof(T).GetProperties()) { if (!reader.IsDBNull(reader.GetOrdinal(prop.Name))) { prop.SetValue(entity, reader[prop.Name]); } } return entity; } return null; }
使用方式:
// 单键查询 var user = await _userRepo.GetByKeyAsync(new { Id = 123 }); // 多键查询 var orderItem = await _orderItemRepo.GetByKeyAsync(new { OrderId = 456, ProductId = 789 });
方案2:使用键值对集合传递参数
适合需要动态生成键条件的场景,明确传递键名和对应值。
接口修改:
public interface IRepository<T> where T : class { // 原有方法... Task<T?> GetByKeysAsync(Dictionary<string, object> keyValues); }
ADO.NET实现示例:
public async Task<T?> GetByKeysAsync(Dictionary<string, object> keyValues) { if (keyValues == null || !keyValues.Any()) return null; var whereClauses = new List<string>(); var parameters = new List<SqlParameter>(); foreach (var kvp in keyValues) { var paramName = $"@{kvp.Key}"; whereClauses.Add($"{kvp.Key} = {paramName}"); parameters.Add(new SqlParameter(paramName, kvp.Value ?? DBNull.Value)); } var sql = $"SELECT * FROM {typeof(T).Name} WHERE {string.Join(" AND ", whereClauses)}"; // 数据库操作和实体映射逻辑同方案1 using var conn = new SqlConnection(_connectionString); await conn.OpenAsync(); using var cmd = new SqlCommand(sql, conn); cmd.Parameters.AddRange(parameters.ToArray()); using var reader = await cmd.ExecuteReaderAsync(); if (await reader.ReadAsync()) { var entity = Activator.CreateInstance<T>(); foreach (var prop in typeof(T).GetProperties()) { if (!reader.IsDBNull(reader.GetOrdinal(prop.Name))) { prop.SetValue(entity, reader[prop.Name]); } } return entity; } return null; }
使用方式:
var keys = new Dictionary<string, object> { { "OrderId", 456 }, { "ProductId", 789 } }; var orderItem = await _orderItemRepo.GetByKeysAsync(keys);
方案3:强类型键对象(类型安全最优解)
为多键实体定义专属键类,确保编译时类型检查,避免拼写错误。
接口修改:
public interface IRepository<T> where T : class { // 原有方法... Task<T?> GetByKeyAsync<TKey>(TKey key) where TKey : notnull; }
定义强类型键类:
// 订单明细复合键 public record OrderItemKey(int OrderId, int ProductId); // 用户单键 public record UserKey(int Id);
ADO.NET实现示例:
逻辑与方案1一致,仅增加泛型约束确保非空:
public async Task<T?> GetByKeyAsync<TKey>(TKey key) where TKey : notnull { var properties = key.GetType().GetProperties(); if (!properties.Any()) return null; var whereClauses = new List<string>(); var parameters = new List<SqlParameter>(); foreach (var prop in properties) { var value = prop.GetValue(key); var paramName = $"@{prop.Name}"; whereClauses.Add($"{prop.Name} = {paramName}"); parameters.Add(new SqlParameter(paramName, value ?? DBNull.Value)); } var sql = $"SELECT * FROM {typeof(T).Name} WHERE {string.Join(" AND ", whereClauses)}"; // 数据库操作和实体映射逻辑同前 using var conn = new SqlConnection(_connectionString); await conn.OpenAsync(); using var cmd = new SqlCommand(sql, conn); cmd.Parameters.AddRange(parameters.ToArray()); using var reader = await cmd.ExecuteReaderAsync(); if (await reader.ReadAsync()) { var entity = Activator.CreateInstance<T>(); foreach (var prop in typeof(T).GetProperties()) { if (!reader.IsDBNull(reader.GetOrdinal(prop.Name))) { prop.SetValue(entity, reader[prop.Name]); } } return entity; } return null; }
使用方式:
// 单键查询 var user = await _userRepo.GetByKeyAsync(new UserKey(123)); // 多键查询 var orderItem = await _orderItemRepo.GetByKeyAsync(new OrderItemKey(456, 789));
方案对比
- 匿名类型:简洁高效,但键名依赖字符串拼写,无编译时检查。
- 键值对集合:动态性强,适合不确定键数量的场景,同样存在键名拼写风险。
- 强类型键对象:类型安全,编译时即可发现错误,适合固定复合键的实体,但需额外定义键类。
所有方案均采用参数化查询,规避SQL注入风险,符合ADO.NET最佳实践。
内容的提问来源于stack exchange,提问作者Amigo
相关产品推荐
相关产品推荐

