Динамически получить DbSet <T>по имени класса Entity - PullRequest
0 голосов
/ 09 октября 2018

Я пытаюсь использовать System.Reflections для динамического получения DbSet<T> от его имени.

Сейчас у меня есть:

  • DbSet name
  • DbSet Type, хранящиеся в переменной

Проблема, с которой я сталкиваюсь, возникает при попытке использовать метод dbcontext.Set<T>(), поскольку (пока это мои попытки):

  • Когда я пытаюсь присвоить <T> мой DbSet Type, он выдает следующую ошибку компиляции:

    "XXXявляется переменной, но используется как тип "

  • Если я попробую использовать оба метода Extension, которые вы найдете ниже в моем коде (который я сделал для того, чтобы попытатьсяполучить IQueryable<T>), он возвращает IQueryable<object>, что, к сожалению, не то, что я ищу, поскольку, конечно, когда я пытаюсь манипулировать им с помощью дальнейших отражений, ему не хватает всех свойств, которыми обладает исходный класс…

Что я делаю не так?Как я могу получить DbSet<T>?

Мой код следующий, но, конечно, дайте мне знать, если вам нужно больше информации, пояснений или фрагментов кода.

Мой контроллерМетод :

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

    //...
}

Класс 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 Добавлен внутренний код TypeFinder ,
Короче говоря, этот метод делает то же самое с Type.GetType, но ищет тип на ВСЕХ сгенерированных сборках

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

ОБНОВЛЕНИЕ , как запрошено в комментариях, вот конкретный случай:

В моей БД у меня есть несколько таблиц, которые действительно похожи друг на друга, поэтому идея состояла в том, чтобы создать метод динамического обновления таблиц, который был бы хорош для каждой таблицы, просто передавая этому методу таблицуимя, идентификатор строки для обновления и JSON, содержащий данные для обновления.
Итак, вкратце, я бы выполнил некоторые обновления таблицы, заданной на входе как тип DbSet, обновив строку с ID==id на входе данными, содержащимися в JSON, которые будут проанализированы внутри объекта типа X(то же самое из dbset) / в словарь.

В псевдокоде:

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

Ответы [ 2 ]

0 голосов
/ 09 октября 2018
returns me an IQueryable<object>, I need an IQueryable<MyType>

Ну, это никогда не сработает.Ваш IQueryable не может иметь тип IQueryable<MyType>, потому что это означало бы, что компилятору нужно было бы знать, что такое MyType, а это невозможно, потому что весь смысл этого упражнения - решить это во время выполнения.

Может быть, этого достаточно, чтобы знать , что эти объекты на самом деле являются экземплярами MyType?

Если нет, я думаю, что вы загнали себя в угол и пытаетесь выяснить, чтокраска, чтобы использовать, чтобы выбраться оттуда.Сделайте шаг назад, это, вероятно, не техническая проблема. Почему вам нужно это сделать?Почему у вас есть конфликтующие потребности в знании типа только во время выполнения и , зная его во время компиляции?

Вам нужно думать о своих требованиях, а не о технических деталях.

0 голосов
/ 09 октября 2018

РЕДАКТИРОВАТЬ: проверено и работает:

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

ВАЖНОЕ ПРИМЕЧАНИЕ: ваш DbContext должен иметь объявленный DbSet для работы!

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