通过实体类名称动态获取DbSet <t>

时间:2018-10-09 13:00:00

标签: c# asp.net-core entity-framework-core system.reflection

我正在尝试使用System.Reflections从其名称动态获取DbSet<T>

我现在得到的是:

  • DbSet名称
  • DbSet的{​​{1}}存储在变量中

我尝试使用Type方法时遇到了我面临的问题,因为(到目前为止,这是我的尝试):

  • 当我尝试将dbcontext.Set<T>() <T>分配给DbSet时,会引发以下编译错误:
      

    “ XXX是变量,但像类型一样使用”

  • 如果我尝试同时使用下面将在代码中找到的两种Type方法(我为了获得Extension而做出),它将返回IQueryable<T> ,不幸的是,这不是我想要的,因为当我尝试进一步进行反射时,它当然缺少原始类具有的所有属性……

我在做什么错?我如何获得IQueryable<object>

以下是我的代码,但是,当然,如果需要更多信息,说明或代码段,请告诉我。

我的控制器的方法

DbSet<T>

ContextSetExtension

public bool MyMethod (string t, int id, string jsonupdate)
{
    string _tableName = t;
    Type _type = TypeFinder.FindType(_tableName); //returns the correct type

    //FIRST TRY
    //throws error: "_type is a variable but is used like a type"
    var tableSet = _context.Set<_type>();  

    //SECOND TRY
    //returns me an IQueryable<object>, I need an IQueryable<MyType>
    var tableSet2 = _context.Set(_type);  

    //THIRD TRY
    //always returns me am IQueryable<object>, I need an IQueryable<MyType>
    var calcInstance = Activator.CreateInstance(_type);
    var _tableSet3 = _context.Set2(calcInstance);

    //...
}

编辑,添加了 TypeFinder 的内部代码。
简而言之,此方法的作用与public static class ContextSetExtension { public static IQueryable<object> Set(this DbContext _context, Type t) { var res= _context.GetType().GetMethod("Set").MakeGenericMethod(t).Invoke(_context, null); return (IQueryable<object>)res; } public static IQueryable<T>Set2<T>(this DbContext _context, T t) { var typo = t.GetType(); return (IQueryable<T>)_context.GetType().GetMethod("Set").MakeGenericMethod(typo).Invoke(_context, null); } } 相同,但是在所有生成的程序集上搜索Type

Type.GetType

更新,具体请参见注释:

在我的数据库中,我有一些表彼此之间非常相似,因此我们的想法是创建一个动态表更新方法,该方法对每个表都适用,只需将表名,ID传递给该方法要更新的行和包含要更新的数据的JSON。
因此,简而言之,我将对输入中作为DbSet类型给出的表进行一些更新,并使用包含在JSON中的数据对输入中的public class TypeFinder { public TypeFinder() { } public static Type FindType(string name) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); var result = (from elem in (from app in assemblies select (from tip in app.GetTypes() where tip.Name == name.Trim() select tip).FirstOrDefault()) where elem != null select elem).FirstOrDefault(); return result; } } 进行更新,以将其解析为一个对象类型X(与dbset相同)/进入字典。

使用伪代码:

ID==id

3 个答案:

答案 0 :(得分:1)

 public
    function validatePlanEntries(Request $request)
    {

        $validation = $this->validate($request, [
            'WithSpouse' => (\Input::has('spouse')) ? 1 : 0;
        ]
}

嗯,那永远都行不通。您的IQueryable不能为returns me an IQueryable<object>, I need an IQueryable<MyType> 类型,因为这意味着编译器将需要知道IQueryable<MyType>是什么,而这是不可能的,因为此练习的全部目的是在运行时确定。 >

也许知道这些对象实际上是MyType的实例?

如果没有,我想您已经把自己粉刷到这里的角落里了,并且您正在尝试找出要用来摆脱该状况的油漆。退后一步,这可能不是技术问题。 为什么,您需要这样做吗?为什么只有在运行时才知道类型而在编译时才知道类型的矛盾需求?

您需要考虑自己的要求,而不是技术细节。

答案 1 :(得分:0)

编辑:已测试并且可以正常运行:

java.lang.RuntimeException: Exception getting JDBC Driver

重要提示:您的DbContext需要声明DbSet正常工作!

public async Task<bool> MyMethod(string _type)
{
    Type type = Type.GetType(_type);

    var tableSet = _context.Set(type);

    var list = await db.ToListAsync();

    // do something
}

// pass the full namespace of class
var result = await MyMethod("Namespace.Models.MyClass")

答案 2 :(得分:0)

我需要从数据库中为已知类型列表中的每种类型动态加载单个记录,以便在管理员编辑模板时打印测试电子邮件,所以我这样做了:

List<object> args = new List<object>();

//...
//other stuff happens that isn't relevant to the OP, including adding a couple fixed items to args
//...

foreach (Type type in EmailSender.GetParameterTypes())
{
    //skip anything already in the list
    if (args.Any(a => a.GetType().IsAssignableFrom(type))) continue;

    //dynamically get an item from the database for this type, safely assume that 1st column is the PK
    string sql = dbContext.Set(type).Sql.Replace("SELECT", "SELECT TOP 1") + " ORDER BY 1 DESC";
    var biff = dbContext.Set(type).SqlQuery(sql).AsNoTracking().ToListAsync().Result.First();
    args.Add(biff);
}

警告:我知道我正在执行此操作的所有实体都至少存在一个记录,并且每种类型只能有一个实例可以传递给电子邮件生成器(它有许多 Debug.Asserts 来测试实施)。

如果您知道要查找的记录 ID,而不是整个表,则可以使用 dbContext.Set(type).Find()。如果您想要任何类型的整个表格,您可以这样做:

string sql = dbContext.Set(type).Sql; //append a WHERE clause here if needed/feasible, use reflection?
var biff = dbContext.Set(type).SqlQuery(sql).ToListAsync().Result;

感觉有点笨重,但它有效。奇怪的是没有没有 Async 的 ToList,但我可以在这里同步运行。在我的情况下,关闭代理创建是必不可少的,但您看起来想要维护上下文状态,以便您可以写回 db。我稍后会做大量的反射,所以我并不真正关心强类型这样的结果集合(因此是 List<object>)。但是一旦你有了集合(即使只是作为对象),你应该能够像你在 UPDATE 示例代码中所做的那样使用 System.Reflection,因为你知道类型并且可以在这样的情况下使用带有已知/给定属性名称的 SetValue方式。

我正在使用 .NET Framework,但希望这可以转换为 .NET Core。