Dynamically get a DbSet<T> by Entity class name

牧云@^-^@ 提交于 2020-03-03 01:12:30

问题


I'm trying to use System.Reflections to get a DbSet<T> dynamically from its name.

What I've got right now is:

  • The DbSet name
  • The DbSet's Type stored on a variable

The issue I'm facing comes out when trying to use the dbcontext.Set<T>() method, since (these are my tries so far):

  • When I try to assign to <T> my DbSet Type, it throws me the following compilation error:

    "XXX is a variable but is used like a type"

  • If I try with using both the Extension methods that you will find below in my code (which I made in order to try to get an IQueryable<T>), it returns a IQueryable<object>, which unfortunately is not what I am looking for, since of course when I try to manipulate it with further Reflections, it lacks of all the properties that the original class has…

What am I doing wrong? How can I get a DbSet<T>?

My code is the following, but of course, let me know if you need more infos, clarifications or code snippets.

My Controller's Method:

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);

    //...
}

Class ContextSetExtension

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);
    }
}

EDIT Added TypeFinder's inner code.
In brief, this method does the same of Type.GetType, but searches Type on ALL the generated assemblies

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;
    }
}

UPDATE as requested in the comments, here's the specific case:

In my DB i've got some tables which are really similar each other, so the idea was to create a dynamic table-update method which would be good for every table, just passing to this method the table name, the ID of the row to update and the JSON containing data to update.
So, in brief, I would perform some updates on the table given in input as DbSet type, updating the row with ID==id in input with the data contained inside the JSON, which will be parsed inside an object of type X(the same of dbset)/into a dictionary.

In pseudo-code:

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

    //THIS DOESN'T WORKS, of course, since as said above:
    //<<throws error: "_type is a variable but is used like a type">>
    var tableSet = _context.Set<_type>();  

    //parsing the JSON
    var newObj = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonupdate, _type);

    //THIS OF COURSE DOESN'T WORKS TOO
    //selecting the row to update:
    var toUpdate = tableSet.Where(x => x.Id == id).FirstOrDefault();

    if(toUpdate!=null)
    {

       var newProperties = newObj.GetType().GetProperties();
       var toUpdateProperties = toUpdate.GetType().GetProperties();

       foreach(var item in properties)
       {
           var temp = toUpdateProperties.Where(p => p.Name==item.Name)
           {
              //I write it really in briefand fast, without lots of checks.
              //I think this is enough, I hope
              temp.SetValue(toUpdate, item.GetValue());
           }
       }

       _context.SaveChanges();
    }

    return false;
}

回答1:


returns me an IQueryable<object>, I need an IQueryable<MyType>

Well, that will never work. Your IQueryable cannot be of type IQueryable<MyType>because that would mean the compiler would need to know what MyType is and that is not possible, because the whole point of this exercise is to decide that on runtime.

Maybe it's enough to know that those objects are in fact instances of MyType?

If not, I think you have painted yourself into a corner here and you are trying to figure out what paint to use to get out of there. Take a step back, it's probably not a technical problem. Why do you need to do this? Why do you have the conflicting needs of knowing the type at runtime only and knowing it at compile time?

You need to think about your requirements, not about the technical details.




回答2:


EDIT: tested and working:

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")

IMPORTANT NOTE: your DbContext need to have the DbSet declared to work!

public class MyContext : DbContext
{
    public DbSet<MyClass> MyClasses { get; set; }
}


来源:https://stackoverflow.com/questions/52721697/dynamically-get-a-dbsett-by-entity-class-name

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!