使用表的字符串名称从 Entity Framework 动态加载表

Dynamically load tables from Entity Framework with string name of tables

假设我有一个字符串列表,表示生成的 类 的名称,其中 Entity Framework 来自 SQL 服务器:

public static List<string> tableList = new List<string>
        {
            "aaa",
            "bbb",
            "ccc",
            "ddd",
            "eee",
        };

我想从实体加载数据:

DateTime from_date = DateTime.MinValue;
DateTime to_date = DateTime.MaxValue;

using (var ctx = new MyEntities())
{
    IList<aaa> aa = ctx.aaa
                       .Where(a => a.date_added >= from_date && 
                                   a.date_added <= to_date)
                       .ToList();
}

但我必须用 20 个或更多 tables 来做到这一点。

有什么方法可以动态地做到这一点吗?喜欢:

Result res = new Result();

List<Result> r = new List<>();

foreach (string table in tableList)
{
    using (var ctx = new MyEntities())
    {
        IList<*table*> aa = ctx.*table*
                               .Where(a => a.date_added >= from_date && 
                                           a.date_added <= to_date)
                               .ToList();

        res.quantity = aa.Count();
        res.title = table;
    }

    r.Add(res);
}

所有需要的 table 都有列 date_added

PS:我只需要计算在定义的时间段内每个 table 中有多少行。比如tables client, customer, employee: 2020年到2021年注册了多少人,我会输出:

{
        "title":"Client",
        "quantity": 19,
        "from_date": [some_date],
        "to_date": [some_date]
},
{
        "title":"Customer",
        "quantity": 123,
        "from_date": [some_date],
        "to_date": [some_date]
},
{
        "title":"Employee",
        "quantity": 31,
        "from_date": [some_date],
        "to_date": [some_date]
},

考虑到您对我的评论的回答,您可以将 classes 添加到 classes 由 EF 生成的命名空间。我建议您在该名称空间中创建一个新接口,如下所示:

public interface IObjectWithDates
{
    DateTime date_added { get; }
}

现在我们希望生成的 classes 具有 date_added 字段来实现 IObjectWithDates 接口。除了修改生成的 classes,您还可以在同一命名空间中添加一个带有

的附加文件
public partial class Client: IObjectWithDates
{ }

public partial class Customer: IObjectWithDates
{ }

//and so on with the classes you want to track

现在我们有一个程序集,其中一些 classes 实现了 IObjectWithDates。如果愿意,您可以使用 Type t = Type.GetType(...) 通过名称获取课程。您还可以要求 C# 为您提供在您的模型命名空间中实现 IObjectWithDates 的所有 classes :

public IEnumerable<Type> AllTrackedClassesInNameSpace(string namespaceName, Assembly assembly)
{
    return assembly.GetTypes()
       .Where(x => ((!string.IsNullOrWhiteSpace(x.Namespace)) && (x.Namespace.ToUpper().Contains(namespaceName.ToUpper()))))
       .Where(x => x.IsClass)
       .Where(x=>typeof(IObjectWithDates).IsAssignableFrom(x));
}

[此处编辑] 此时,您将拥有一个类型列表(以下代码中的IEnumerable<Type> typeList),所有类型都实现了 IObjectWithDates,您应该能够做到这一点:

public int MyCount<T>(DbContext ctx) where T: class, IObjectWithDates
{
    return ctx.Set<T>().Count(a => a.date_added >= from_date && a.date_added <= to_date);
}

然后

Result res = new Result();

List<Result> r = new List<Result>();

using (var ctx = new DbContext(""))
{
    foreach (var type in typeList)
    {
        var method = this.GetType().GetMethod("MyCount").MakeGenericMethod(type);

        res.quantity = (int)method.Invoke(this, new object[]{ ctx });
        res.title = table;
    }

    r.Add(res);
}

一些细节可能需要调整(from_date / to_date 可能是 MyCount 的参数或调用 class 的属性),但全局思想就在那里。您可以通过反射在上下文中获取 Set<>() 方法,但在我看来,单独的方法更易于阅读和调试。

请注意,生成对象的 IList<> 只是为了计算它们是不明智的,因为 SQL 将请求所有对象,然后 EF 将在每行上实例化很多实例...只是为了计算它们而不是使用 Select Count...