繁体   English   中英

如何在运行时将通过反射获取的属性转换为正确的泛型类型

[英]How to cast down a property fetched with reflection to a its correct generic type at runtime

简而言之,我要实现的是根据配置在我的(ef 核心) DbContext中验证特定的DbSet 这意味着我想在配置中指定我想要验证的表,然后我会将验证规则应用于我的DbContext DbSet 请允许我解释一下:

假设您在配置中有以下值作为 JSON 数组:

"TablesToValidate" : [
    "Appointments",
    "Products"
]

这些值映射到以下内容:

public class AppSettings {
    public IEnumerable<DatabaseTable> TablesToValidate { get; set; }
}

其中DatabaseTable是一个 Enum,所有表名都作为 Enum 值:

[JsonConverter(typeof(StringEnumConverter))]
public enum DatabaseTable {
    Appointments,
    Products,
    Users
}

这是 DbContext 的简化版本:

public DbContext(....)
{
    DbSet<Appointment> Appointments { get; set; }
    DbSet<Product> Products { get; set; }
    DbSet<User> Users { get; set; }
}

应该发生的是,对于从配置中获取的每个DatabaseTable ,我需要从DbSet中获取与其名称匹配的DbContext并验证它不为空。 这是我到目前为止的代码以及我遇到的问题:

appSettings
    .TablesToValidate
    .ToList()
    .Foreach(table => {
        var propertyInfo = dbContext //an instance of the DbContext
                        .GetType()
                        .GetProperty(table.ToString());

        // let's say propertyInfo is not null

        var dbSet = propertyInfo.GetValue(dbContext) as DbSet<?>; //Consider this line
        
        if (dbSet is null || !dbSet.Any())
        {
            //db set not valid. Do Something
        }
    });

如您所见,我想对获取的DbSet执行的操作是检查它是否为 null (如果我将其转换为object将起作用)和dbSet.Any()这是主要问题。

无论我采取什么路线,我仍然需要 DbSet 的实际泛型类型才能在dbSet变量上调用Any() function。 我不能将运行时类型放在通用定义<>中,因为它需要编译时类型。 我还尝试将dbSet变量强制转换为DbSet<BaseEntity> ,其中BaseEntity是所有AppointmentProductUser的父级,但我怀疑它不起作用并且它将返回 null 因为强制转换总是会失败。

关于如何解决这个问题的任何想法?

您将无法转换为“未知泛型”,然后将其作为参数传递给 function。 但是您可以通过反射调用具有匹配参数的通用 function。 不过看起来不会很漂亮。

// first, we construct the generic type of the parameter that goes to AnyAsync.
// myType is the type of the entity for the given DbSet.
var parameterType = typeof(IQueryable<>).MakeGenericType(myType);
// second, we extract the needed method from the EF extensions.
// make sure to get the correct overload, we need the one without the predicate.
var methodInfo = typeof(EntityFrameworkQueryableExtensions)
                    .GetMethod(nameof(EntityFrameworkQueryableExtensions.AnyAsync),
                               1,
                               new[] { parameterType, typeof(System.Threading.CancellationToken) })
                    .MakeGenericMethod(myType);
// invoke the method with the untyped parameters
var result = await (Task<bool>)methodInfo
                       .Invoke(null, 
                               new object[] { dbSet, default(System.Threading.CancellationToken) });

尝试转换为非通用IEnumerable 然后,您可以使用foreach查看它是否为空。

var dbSet = propertyInfo.GetValue(dbContext) as IEnumerable;

bool isEmpty = true;
foreach (var item in dbSet) { isEmpty = false; break; }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM