Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.80/5: Рейтинг темы: голосов - 5, средняя оценка - 4.80
 Аватар для werymag
26 / 11 / 1
Регистрация: 20.05.2015
Сообщений: 217

Рефлексия обобщенных коллекций для вызова методов при неизвестном типе

08.06.2022, 18:01. Показов 1406. Ответов 23

Студворк — интернет-сервис помощи студентам
Если вкратце: Использую Entity Framework в котором используется множество коллекций сущностей DbSet;

Все коллекции разные, но почти все имеют общую суть (обрабатываются одинаково).

Вопрос, как мне через рефлексию сделать возможным работу со всеми коллекциями одним кодом (типа Load, преобразование в таблицу, Save и прочие непотребства). Можно, конечно, каждый раз все таблицы перечислять, но как-то это не по програмерски.

Абстрактный нерабочий пример (так как в метод расширение Load надо передать обобщение с аргументом):

C#
1
2
3
4
5
6
7
8
9
 public class MyDbContext: DbContext
    {
        public DbSet<a> A{ get; set; }
        public DbSet<b> B{ get; set; }
        public DbSet<c> C{ get; set; }
        public DbSet<d> D{ get; set; }
        public DbSet<e> E{ get; set; }
// и т.д.
   }
C#
1
2
3
4
5
6
7
8
9
10
11
               var props = typeof(MyDbContext).GetProperties();
                foreach (var prop in typeof(MyDbContext).GetProperties())
                {                    
                    if (prop.PropertyType.Name == "DbSet`1")
                    {
                        Type? type = prop.PropertyType.GenericTypeArguments[0]; //Вот его бы как-нибудь в обобщение передать как аргумент.
                        var source = prop.GetValue(ap);
                        if (source != null)
                            EntityFrameworkQueryableExtensions.Load(source); //просто расширение для примера, тут могут быть иные действия, но все они требуют тип аргумента
                    }
                }
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
08.06.2022, 18:01
Ответы с готовыми решениями:

Рефлексия обобщенных типов
Допустим созали какой то тип, оформили как библиотеку Class Library скомплировали, затем загрузили динамически и создали тип и затем...

Алгоритм для вызова методов с массива
Заранее извините за возможную не точною формулировку допустим.. у меня есть массив экземпляров класса Student у каждого студента есть...

Специальные методы для вызова необъявленных методов объекта
Существуют ли в Java функции аналогичные __call() в php, т.е. если я обращаюсь к несуществующему методу управление передается в __call(). ...

23
 Аватар для werymag
26 / 11 / 1
Регистрация: 20.05.2015
Сообщений: 217
20.11.2022, 14:21  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Элд Хасп Посмотреть сообщение
werymag, рефлексия здесь допустма.
Пример https://stackoverflow.com/a/564373/13349759
В этом примере надо только добавить сохранение полученных рефлексией данных.
И следующий раз использовать не рефлексию, а уже сохранённые данные.
Сеттеры и Геттеры свойств хранить как делегаты Action и Func<object>.
Ну в целом я это уже и так реализовал своими корявыми руками . Это-то относительно просто.
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
      public List<T> TableToList<T>(DataTable table)
            where T : new()
        {
            List<T> elements = new List<T>();           
            foreach (DataRow row in table.Rows)
            {
                T elem = new();
                elements.Add(elem);
                foreach (var prop in typeof(T).GetProperties())
                    if (table.Columns.Contains(prop.Name.Replace('_', ' ')))
                    {
                        var value = row[prop.Name.Replace('_', ' ')];
                        prop.SetValue(elem, value is DBNull ? null : value);
                    }
            }
            return elements;
        }
        public WellDataTable ListToTable<T>(TableDataSet set, List<T> objectsList)
        {
 
            WellDataTable table = new WellDataTable(typeof(T).Name.Replace('_', ' '), set, true);
            var properties = typeof(T).GetProperties();
 
            foreach (PropertyInfo property in properties)
            {
                WellDataColumn newColumn = new WellDataColumn(
                    property.Name.Replace('_', ' '),
                    Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType);
                table.Columns.Add(newColumn);
            }
 
            foreach (T objectDB in objectsList)
            {
                DataRow row = table.NewRow();
                table.Rows.Add(row);
                foreach (PropertyInfo property in properties)
                    row[property.Name.Replace('_', ' ')] = property.GetValue(objectDB) ?? DBNull.Value;
 
            }
            return table;
        }

Можно, конечно попробовать эту библиотеку мета программирования, но что-то лень пока для столь малой задачи ...
Думал может есть более элегантные способы без 20


Честно признаться не понимаю этих претензий к скорости работы рефлексии с в контексте работы с DataTable с её распаковкой/запаковкой типов. Мне кажется заполнение средних размеров таблицы всё равно будет на порядок дольше, чем работа рефлексии (возможно ошибаюсь).

В целом то всё равно в обоих периметрах по ссылке используются джейнерики, то есть всё равно надо явно указать тип членов списка, что опять приводт к необходимости использования 20 строк кода в место одной (о ужас ), то есть к тому же, что я написал выше (правда согласен, надо вытащить конвертацию)
Цитата Сообщение от Элд Хасп Посмотреть сообщение
В каждой таблице только отражение одной сущности?
Или разные строки нужно в разные сущности отражать?
Строго одной, таблицы и сущностью полностью одинаковые, у меня даже скриптик есть, который из всех таблиц генерирует класс со всеми сущностями, эдакий TableFirst
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
20.11.2022, 15:26
Цитата Сообщение от werymag Посмотреть сообщение
public List<T> TableToList<T>(DataTable table)
Если правильно понял.
Вы получаете в DataTable данные одной таблицы БД.
Соответственно, TableName должен содержать имя этой таблицы.
И по имени таблицы из DbContext можно получить тип сущности для этой таблицы.

То есть в обобщённом методе, как мне кажется, нет необходимости.
Так же раз этот тип будет из DdContext, то он гарантированно содержит конструктор по умолчанию.

Цитата Сообщение от werymag Посмотреть сообщение
WellDataTable table = new WellDataTable(typeof(T).Name.Replace('_' , ' '), set, true);
Вот здесь бы я предпочёл сохранить имя таблицы БД в оригинале.
Его нужно или явно передать, или получить из атрибута сущности.
Замена символов, возможно, нужна для Представления, ну там и стоит это преобразование проводить.

Добавлено через 5 минут
Цитата Сообщение от werymag Посмотреть сообщение
List<T> objectsList
Здесь нужен IList, или IEnumerable без дженерика.
И рефлексией проверять, есть дженерик или нет.
И если есть, то сущность это DbContext или нет.
Для сущности DbContext получать имя таблицы из DbContext.
Для остальных - проверять наличие атрибута [Table], для свойств наличие атрибута [Column].

Добавлено через 2 минуты
Цитата Сообщение от werymag Посмотреть сообщение
В целом то всё равно в обоих периметрах по ссылке используются дженерики,
От дженериков лучше избавиться.
Выше описал примерный путь.
Сделать нужно на "чистой рефлексии".
Можно для удобства использования потом добавить перегрузки с дженериками.

Добавлено через 6 минут
TableToList, ListToTable обычно не указывается из чего конвертируется.
И лучше сделать как статические методы расширения.
То есть более типично: public static IList ToList(this DataTable table) и public static WellDataTable ToTable(this IEnumerable objects).

Если добавится проверка согласованности с DbContext, то тогда можно передавать дженериком тип DbContext.
1
 Аватар для werymag
26 / 11 / 1
Регистрация: 20.05.2015
Сообщений: 217
20.11.2022, 16:25  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
То есть в обобщённом методе, как мне кажется, нет необходимости.
Так же раз этот тип будет из DdContext, то он гарантированно содержит конструктор по умолчанию.
Мозг плавиться...
такой вариант ругается, что нельзя динамически создать DBSet (WellID - это базовый класс для всех таблиц с полями имя и ID)

C#
1
2
3
4
      Type typeDBSet = this.GetType().GetProperty(table.TableName.Replace(' ', '_')).PropertyType;
         Type entityType = typeDBSet.GetGenericArguments().Single();
 
          DbSet<WellID>? elements = Activator.CreateInstance(typeDBSet) as DbSet<WellID>;
Такой вариант с форума ругается на неоднозначность совпадения метода с именем Set....

C#
1
2
3
4
5
6
7
8
            Type typeDBSet = this.GetType().GetProperty(table.TableName.Replace(' ', '_')).PropertyType;
            Type entityType = typeDBSet.GetGenericArguments().Single();
 
            MethodInfo? dbSetMethodInfo = typeof(DbContext).GetMethod("Set");
            dynamic dbSet = dbSetMethodInfo.MakeGenericMethod(entityType).Invoke(this, null);
            dynamic Rec = Activator.CreateInstance(entityType);
            Rec.active = true;
            dbSet.Add(Rec);
В целом я тут подумал... 20 строчек кода с джейнериком не так уж плохо, как-то прям больно всё это выглядит

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Здесь нужен IList, или IEnumerable без джененрика.
И рефлексией проверять, есть дженерик или нет.
И если есть, то сущность это DbContext или нет.
Для сущности DbContext получать имя таблицы из DbContext.
Для остальных - проверять наличие атрибута [Table], для свойств наличие атрибута [Column].
Ага, спасибо большое, поправлю, да это я на ходу между написаниями сообщений сделал, как мысль в голову легла. Надо, конечно, править.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
TableToList, ListToTable обычно не указывается из чего конвертируется.
Спасибо, буду знать!

Но в целом задача, конечно, не стоит затраченных усилий, уже больше спортивный интерес .
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
20.11.2022, 16:34
Цитата Сообщение от werymag Посмотреть сообщение
задача, конечно, не стоит затраченных усилий, уже больше спортивный интерес
Я так и понял.
Рефлексия чаще всего и используется в библиотеках на целенных на широкое использование во избежание явных зависимостей.

Цитата Сообщение от werymag Посмотреть сообщение
Мозг плавиться...
такой вариант ругается, что нельзя динамически создать DBSet
Вы имя таблицы хотите получить?

Если да, то это данные БД. А с БД работает DbContext, а не DbSet.
DbSet для конкретной БД подготавливается DbContext'ом.

Поэтому вам нужно передавать SomeMethod<TDbContext>(....) where TDbContext : DbContext, new().
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
20.11.2022, 16:34
Помогаю со студенческими работами здесь

Рефлексия методов и конструкторов
Задание: нужно получить методы и конструкторы (с аргументами) из класса предметной области и класса CheckBox. Все это выполнил и работает,...

Наследование,переопределение методов[рефлексия]
Раньше я практически не сталкивался с рефлексией, но теперь появилась острая проблема .Загружаю сборку с помощью Assembly.LoadFrom и мне...

Какой алгоритм выбрать для меньшего кол-во вызова api-методов?
Всем привет:) Мне нужно сделать интеграцию моего сервиса с другим. Что у меня есть: сторонние api методы. Мне нужно сделать минимальным...

Рефлексия методов с неизвестным массивом параметров
Здравствуйте! Подскажите, как мне передать параметры в getMethod, для выполнения этого метода через invoke, если входящие параметры заранее...

Рефлексия. Вывод списка методов, свойств
Здравствуйте. Задание по рефлексии, я в ней вообще не разбираюсь. Итак, задание следующее: Я кое-как написал код для вывода списка...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
24
Ответ Создать тему
Новые блоги и статьи
[golang] Двоичная куча, min-heap
alhaos 20.05.2026
Двоичная куча Двоичная куча — структура данных, которая всегда держит самый важный элемент наготове. Представьте очередь к хилеру в игре, и очередь из игроков в приоритете те у кого меньше. . .
[golang] Breadth-First Search
alhaos 19.05.2026
BFS (Breadth-First Search) — это базовый алгоритм обхода графа в ширину, который поуровнево исследует все связанные вершины. Он начинает с выбранной точки и проверяет всех соседей, прежде чем. . .
[golang] Алгоритм «Хак Госпера»
alhaos 17.05.2026
Алгоритм «Хак Госпера» Хак Госпера (Gosper's Hack) — алгоритм нахождения следующего по величине числа с тем же количеством установленных бит. Придуман Биллом Госпером в 1970-х, опубликован в. . .
Рисование бинарного древа до 6-го колена на js, svg.
russiannick 17.05.2026
<svg width="335" height="240" viewBox="0 0 335 240" fill="#e5e1bb"> <style> <!]> </ style> <g id="bush"> </ g> </ svg> function fn(){ let rost;/ / высота древа let xx=165,yy=210,w=256;
FSharp: interface of module
DevAlt 16.05.2026
Интерфейс модуля F# позволяет управлять доступностью членов, содержащихся в реализации модуля. По-умолчанию все члены модуля доступны: module Foo let x = 10 let boo () = printfn "boo" . . .
Хитросплетение родственных связей пантеона греческих богов.
russiannick 14.05.2026
Однооконник, позволяющий узреть и изучить отдельных героев древней Греции. <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible". . .
[golang] Угол между стрелками часов
alhaos 12.05.2026
По заданным значениям часа и минуты необходимо определить значение меньшего угла между стрелками аналогового циферблата часов. import "math" func angleClock(hour int, minutes int) float64 { . . .
Debian 13: Установка Lazarus QT5
ВитГо 09.05.2026
Эта инструкция моя компиляция инструкций volvo https:/ / www. cyberforum. ru/ blogs/ 203668/ 10753. html и его же старой инструкции по установке Lazarus с gtk2. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru