You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 00:34:59