首页 > 解决方案 > 不知道如何遍历 DbContext 中的类型,一般调用 ToListAsync

问题描述

我有一个带有国家属性的 DbContext

public DbSet<Country> Countries { get; set; }

我想使用反射ToListAsync在我的上下文中调用每种类型的方法

我正在研究一种方法,该方法将对 DbContext 中的所有表进行快照,包括过滤表达式。已经使用 EntityQueryFilterHelper 处理了过滤

我正在为 .NET Core 3.1 使用 Ef Core

感谢答案中的帮助,我现在有了代码

var contextDictionary = new Dictionary<string, object>();
var allCountries = await this.Countries.ToListAsync();
var firstCountry = allCountries.FirstOrDefault();

var filters = new List<FilterObject>
{
    new FilterObject() {PropertyName = "Name", PropertyValue = firstCountry.Name}
};

var entityTypes = this.Model.GetEntityTypes().Where(x => x.Name.Contains("Country"));

foreach (var entityType in entityTypes)
{
    var requiredType = entityType.GetType();
    var filter = EntityQueryFilterHelper.CreateFilter<Country>(filters);
    var allItems = from x in this.Countries select x;
    var filteredItems = filter(allItems);
    var method = typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ToListAsync))?.MakeGenericMethod(typeof(Country));
    var toListAsyncResult = (Task<List<Country>>) method?.Invoke(null, new object[] {filteredItems, default(CancellationToken)});
    if (toListAsyncResult != null)
    {
        var result = await toListAsyncResult;
        contextDictionary.Add("Countries", result);
    }
}

这完美地工作 - 所以下一个问题是如何更改上述内容以使用 EntityType 而不是硬编码

有人可以帮忙吗?

保罗

标签: c#reflectionentity-framework-core

解决方案


ToListAync()课堂EntityFrameworkQueryableExtensions上,所以

var method = typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ToListAsync)).MakeGenericMethod(typeof(Country));

它是static,所以:

var result = method.Invoke(null, new object[] { this.Countries, default(CancellationToken) });

哪里resultTask<List<Country>>一个Task

调用它很困难 :-) 从技术上讲,调用该方法很容易……提取结果很难。如果您想完全动态化,它会很快变得困难:

var t = (Task)method.Invoke(null, new object[] { context.Blogs, default(CancellationToken) });
await t;
var ilist = (IList)((dynamic)t).Result;

现在你是一个非泛型的IList,所以它的元素是object. 但最终你走错了方向。如果您想朝着“正确”的方向前进,请执行以下操作:

public static async void DoWorkOnMyIQueryable<T>(IQueryable<T> dbset)
{
    List<T> res = await dbset.ToListAsync();

    foreach (var el in res)
    {
        // Do something
    }
}

并调用DoWorkOnMyIQueryable通过反射。现在至少你有一个 typed List<T>

现在您已经写出了完整的问题,通常情况下,这是一个XY 问题

正如我所写的,构建一个基于泛型的单个泛型方法TEntity然后使用反射来调用它要容易得多。

方法:

public static class Tools
{
    public static async Task<IList> ExtractData<TEntity>(DbContext context, List<FilterObject> filters) where TEntity : class
    {
        var dbset = context.Set<TEntity>();
        var filter = EntityQueryFilterHelper.CreateFilter<TEntity>(filters);
        var filtered = filter(dbset);

        return await filtered.ToListAsync();
    }
}

现在称之为:

public async Task<Dictionary<string, IList>> GenerateContextDictionary()
{
    var filters = new List<FilterObject>();
    // Here prepare your filters

    var entityTypes = this.Model.GetEntityTypes().Where(x => x.Name.Contains("Country"));

    var method = typeof(Tools).GetMethod(nameof(Tools.ExtractData), BindingFlags.Public | BindingFlags.Static);

    var contextDictionary = new Dictionary<string, IList>();

    foreach (var entityType in entityTypes)
    {
        var method2 = method.MakeGenericMethod(entityType.ClrType);
        var ilist = await (Task<IList>)method2.Invoke(null, new object[] { this, filters });
        contextDictionary.Add(entityType.Name, ilist);
    }

    return contextDictionary;
}

推荐阅读