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

能否在EF Core中使用泛型类型的DbSet,实现动态查询统一前缀表单表数据?

问题原因
  • 泛型参数推导错误:调用RestoreFormInfo时传入的参数是Type类型实例,导致泛型参数TEntity被识别为System.Type,你的DbContext中没有对应Type的DbSet,因此抛出异常。
  • 实体类型获取错误:原有代码中_context.GetType().GetProperty(formName).GetType()拿到的是PropertyInfo的类型,不是表单对应的实体类型。
  • 异步逻辑未正确处理:FirstAsync()返回的是Task<T>对象,你未等待执行完成就直接强转为IList,也会导致运行时错误。
  • SQL注入风险:直接拼接SQL参数,存在安全隐患。
修复方案

首先是获取所有表单实体类型的逻辑:

// 方式1:通过DbContext模型获取,更稳定
var formEntityTypes = _context.Model.GetEntityTypes()
    .Where(t => t.ClrType.Name.StartsWith("Form"))
    .ToList();

// 方式2:如果要沿用你原来的反射属性逻辑
var formInfos = _context.GetType().GetProperties()
    .Where(p => p.Name.StartsWith("Form") && p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
    .Select(p => new {
        FormName = p.Name,
        EntityType = p.PropertyType.GetGenericArguments()[0]
    })
    .ToList();

然后是遍历查询的完整实现:

foreach (var formTypeInfo in formEntityTypes)
{
    var entityClrType = formTypeInfo.ClrType;
    // 获取非泛型DbSet
    var dbSet = _context.Set(entityClrType);
    
    // 构造参数化SQL,避免注入
    var tableName = formTypeInfo.GetTableName();
    var sql = $"SELECT * FROM {tableName} WHERE PacientID = @p0 AND AppointmentID = @p1";
    var sqlParams = new object[] { pacientID, appointmentID };
    
    // 反射调用FromSqlRaw
    var fromSqlMethod = typeof(RelationalQueryableExtensions)
        .GetMethods(BindingFlags.Public | BindingFlags.Static)
        .First(m => m.Name == nameof(RelationalQueryableExtensions.FromSqlRaw) && m.GetParameters().Length == 3);
    var genericFromSql = fromSqlMethod.MakeGenericMethod(entityClrType);
    var query = (IQueryable)genericFromSql.Invoke(null, new object[] { dbSet, sql, sqlParams });
    
    // 添加无跟踪配置
    query = query.AsNoTracking();
    
    // 调用FirstAsync获取结果
    var firstAsyncMethod = typeof(EntityFrameworkQueryableExtensions)
        .GetMethods(BindingFlags.Public | BindingFlags.Static)
        .First(m => m.Name == nameof(EntityFrameworkQueryableExtensions.FirstAsync) && m.GetParameters().Length == 1);
    var genericFirstAsync = firstAsyncMethod.MakeGenericMethod(entityClrType);
    var resultTask = (Task)genericFirstAsync.Invoke(null, new object[] { query });
    await resultTask;
    
    // 拿到最终的表单实体对象
    var formEntity = resultTask.GetType().GetProperty("Result").GetValue(resultTask);
    
    // 此处可以根据你的需求处理formEntity,比如存入集合统一返回
}
优化建议

如果所有表单实体都有相同的公共属性(比如PacientID、AppointmentID),可以定义一个公共接口让所有表单实体实现,比如:

public interface IAttendanceForm
{
    public string PacientID { get; set; }
    public int AppointmentID { get; set; }
}

拿到formEntity后可以直接转成IAttendanceForm处理公共属性,不需要额外反射。

内容的提问来源于stack exchange,提问作者Renan Oliveira

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 11:45:03