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

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

08.06.2022, 18:01. Показов 1533. Ответов 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
27 / 12 / 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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,175
Записей в блоге: 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
27 / 12 / 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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,175
Записей в блоге: 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
Ответ Создать тему
Новые блоги и статьи
Nekobox - outbounds[0].transport: unknown transport type: raw
damix 01.10.2026
Фикс ошибки Правым кликом по серверу -> отладочная информация -> edit Заменить "net": "raw", на "net": "tcp", Нажать кнопку reload.
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru