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

如何基于参数动态切换LINQ查询的同结构日志表数据源

解决思路

首先要做的核心准备:给所有结构相同的日志实体定义一个公共接口(比如ILogEntry),包含查询用到的所有属性(parentid、transactiondate、oldvalue等),让每个日志实体类(如Wolog、OrderLog)都实现这个接口,这样就能统一后续的数据源类型。

下面是三种可行的实现方案,按需选择:

方案1:条件分支直接返回数据源(适合日志表数量少的场景)

直接通过switch/if-else根据参数返回对应DBContext集合,统一为IQueryable<ILogEntry>类型,后续查询逻辑完全复用。

示例代码:

// 定义公共接口
public interface ILogEntry
{
    int parentid { get; set; }
    DateTime transactiondate { get; set; }
    string oldvalue { get; set; }
    string newvalue { get; set; }
    string field { get; set; }
    string tableaction { get; set; }
}

// 让日志实体实现接口(以Wolog为例)
public class Wolog : ILogEntry
{
    // 原有属性,确保与接口定义一致
    public int parentid { get; set; }
    public DateTime transactiondate { get; set; }
    public string oldvalue { get; set; }
    public string newvalue { get; set; }
    public string field { get; set; }
    public string tableaction { get; set; }
    // 其他实体属性...
}

// 获取动态数据源的方法
private IQueryable<ILogEntry> GetLogDataSource(string logType)
{
    return logType switch
    {
        "WO" => this.DBContext.wologs.AsQueryable<ILogEntry>(),
        "Order" => this.DBContext.orderlogs.AsQueryable<ILogEntry>(),
        // 新增日志表时在这里加分支
        _ => throw new ArgumentException("无效的日志类型")
    };
}

// 复用查询逻辑
public IQueryable<dynamic> GetLogData(string logType, string primaryKey)
{
    var dataSource = GetLogDataSource(logType);
    var decryptedParentId = Convert.ToInt32(Encryption.Decrypt(primaryKey));
    
    return from l in dataSource
           where l.parentid == decryptedParentId
           orderby l.transactiondate
           select new
           {
               chartdate = l.transactiondate,
               tabledate = l.transactiondate.ToString("MM/dd/yyyy hh:mm tt"),
               oldvalue = l.oldvalue ?? "",
               newvalue = l.newvalue ?? "",
               l.field,
               l.tableaction
           };
}

方案2:反射动态获取数据源(适合日志表数量多的场景)

如果日志表数量多,不想每次加表都修改分支逻辑,可以用反射从DBContext中动态获取对应集合,同样返回IQueryable<ILogEntry>。

示例代码:

private IQueryable<ILogEntry> GetLogDataSource(string logType)
{
    // 假设表名规则是「logType小写 + logs」,比如WO对应wologs
    var propertyName = $"{logType.ToLower()}logs";
    var dbSetProperty = this.DBContext.GetType().GetProperty(propertyName);
    
    if (dbSetProperty == null)
        throw new ArgumentException($"找不到对应日志表:{propertyName}");
    
    var dbSet = dbSetProperty.GetValue(this.DBContext) as IQueryable<ILogEntry>;
    return dbSet ?? throw new InvalidOperationException("该日志表未实现ILogEntry接口");
}

// 后续查询逻辑和方案1完全一致

方案3:EF Core泛型Set结合类型映射(类型更安全的方案)

维护一个日志类型到实体类型的映射字典,用EF Core的Set<T>方法动态获取数据源,类型安全性更高。

示例代码:

// 提前定义日志类型与实体类型的映射
private static readonly Dictionary<string, Type> _logTypeMap = new()
{
    { "WO", typeof(Wolog) },
    { "Order", typeof(OrderLog) },
    // 新增日志表时添加映射
};

private IQueryable<ILogEntry> GetLogDataSource(string logType)
{
    if (!_logTypeMap.TryGetValue(logType, out var entityType))
        throw new ArgumentException("无效的日志类型");
    
    // 调用DbContext的Set<T>方法获取集合
    var setMethod = typeof(DbContext).GetMethod(nameof(DbContext.Set)).MakeGenericMethod(entityType);
    var dbSet = setMethod.Invoke(this.DBContext, null) as IQueryable<ILogEntry>;
    
    return dbSet ?? throw new InvalidOperationException("该日志表未实现ILogEntry接口");
}

// 查询逻辑复用方案1的代码

注意事项

  • 所有日志实体必须严格实现公共接口,属性名称、类型必须完全匹配,否则LINQ查询会报错。
  • 若使用EF Core,需确保DbContext中对应的DbSet已正确配置。
  • 反射方案性能略低于条件分支,但日志表数量多的时候更易维护。

内容的提问来源于stack exchange,提问作者Stephan Young

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 22:16:01