It will be a bit too much code, but do not worry =)
In, let's say, the application's data model is ("tables") a collection of instances of classes corresponding to certain database tables; they are updated from the database by Dapper, with a hard indication of the class in which instances the mapping will be used to place the results of the query, respectively, for each "table" there is a typical function that differs only in the name of the base entity. Naturally, this is not beautiful, and I want to summarize

public static OracleConnection oracleConnect;//общедоступный коннект к БД Оракл public ObservableCollection<LOGS> Logs;//"таблица" записей лога public IEnumerable<LOGS> getLogsRows(string sqlWhere = @"")//функция её обновления { string oracleSelectAllLogs = "SELECT * FROM LOGS " + sqlWhere; var getLogs = MNLZContext.oracleConnect.Query<LOGS>(oracleSelectAllLogs);//выбрать всё содержимое табл. return (getLogs); } public ObservableCollection<TRACK_EVENT> TrackEvent;//"таблица" событий public IEnumerable<TRACK_EVENT> getTrackEventRows(string sqlWhere = @"")//функция её обновления { string oracleSelectAllTrackEvent = "SELECT * FROM TRACK_EVENT " + sqlWhere; var trackEvent = MNLZContext.oracleConnect.Query<TRACK_EVENT>(oracleSelectAllTrackEvent);//выбрать всё содержимое табл. return (trackEvent); } //неудачная пока попытка "обобщения" public Dictionary<string, object> dbTables;//набор всех "таблиц", с доступом по имени табл. в БД public IEnumerable<baseTable> getTableRows(string tableName, string sqlWhere=@"") {//!собственно ПРОБЛЕМА: тут надо перейти от "baseTable" к чему-то вроде Type tableType = ((ObservableCollection<object>) dbTables[tableName]).GetType();//получим класс сущности элемента перечисления (тип таблицы), надеюсь //и далее вместо "baseTable" писать вроде "tableType.GetType()" -- но оно не работает, т.к. перечисления IEnumerable<T> недопустимы в данном контексте =( string oracleSelectAllTableRows = @"SELECT * FROM " + tableName + ' ' + sqlWhere; IEnumerable<baseTable> tableRows = MNLZContext.oracleConnect.Query<baseTable>(oracleSelectAllTableRows);//тут будет ошибка "перечисления IEnumerable<T> недопустимы в данном контексте", если пытаться заменить baseTable на tableType return (tableRows); } 

JFYI:
I suggest using it this way:

 dbTables.Add("LOGS", Logs);//"таблицы" в словарь, с доступом по ключу == имени табл. в БД dbTables.Add("TRACK_EVENT", TrackEvent); foreach(string tableName in dbTables.Keys)//разом все "таблицы" обновить из БД {//так работать не будет, это просто примерный набросок dbTables[tableName] = new ObservableCollection<baseTable>( getTableRows(tableName) ); }; 

DB entity classes:

 public partial class baseTable : Object //просто базовый класс для всех таблиц БД (пока только: LOGS, TRACK_EVENT) { public int ID { get; set; }//Первичный ключ } public partial class LOGS : baseTable //запись лога {//...с какими-то полями, это не существенно } public partial class TRACK_EVENT : baseTable //отслеживаемое событие {//...с какими-то полями, это не существенно } 

It seems to be a simple question, but something I don’t think - tell me how to wriggle out?

    1 answer 1

    If I correctly understood what exactly you want to get, then you cannot do without reflection. And it seems that you are a little confused in the types when writing your own version of the code.

    By the way, see: first you have an ObservableCollection<itemType> object in dbTables[tableName] , and then replace it with ObservableCollection<baseTable> . This is not very good: such a system will withstand only a one-time update of data, and on the 2nd update attempt it will break. It would be necessary to replace dbTables[tableName] with an object similar in type. I suggest returning from the getTableRows() method not IEnumerable<baseTable> , but immediately ObservableCollection<itemType> . Well, since we do not know in advance what type the returned ObservableCollection<> will be refined, the function itself will return an object .

    As a result, I propose such code for the getTableRows method:

     public object getTableRows(string tableName, string sqlWhere = @"") { // взять тип таблицы, сидящей в dbTables[tableName] Type tableType = dbTables[tableName].GetType(); // проверить полученный тип if (tableType.GetGenericTypeDefinition() != typeof(ObservableCollection<>)) throw new ArgumentException("Непонятный класс элемента dbTables[tableName]"); // взять единственный generic-аргумент сконструированного класса ObservableCollection<T> Type itemType = tableType.GetGenericArguments()[0]; // получить обобщённый метод SqlMapper.Query<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null) MethodInfo generic_method = typeof(SqlMapper).GetMethods() .Where(m => m.Name.Equals("Query")) .Where(m => m.IsStatic) .Where(m => m.GetParameters().Length == 7) .Where(m => m.GetParameters()[0].ParameterType == typeof(IDbConnection)) .Where(m => m.GetParameters()[1].ParameterType == typeof(string)) .Where(m => m.GetParameters()[2].ParameterType == typeof(object)) .Where(m => m.GetParameters()[3].ParameterType == typeof(IDbTransaction)) .Where(m => m.GetParameters()[4].ParameterType == typeof(bool)) .Where(m => m.GetParameters()[5].ParameterType == typeof(int?)) .Where(m => m.GetParameters()[6].ParameterType == typeof(CommandType?)) .Where(m => m.ContainsGenericParameters) .Single(); // создать сконструированный метод SqlMapper.Query<itemType>(IDbConnection, string, object, IDbTransaction, bool, int?, CommandType?) MethodInfo constructed_method = generic_method.MakeGenericMethod(new Type[] { itemType }); // строка запроса string oracleSelectAllTableRows = @"SELECT * FROM " + tableName + ' ' + sqlWhere; // формируем список параметров для вызова object[] call_parameters = new object[] { MNLZContext.oracleConnect, oracleSelectAllTableRows, constructed_method.GetParameters()[2].DefaultValue, constructed_method.GetParameters()[3].DefaultValue, constructed_method.GetParameters()[4].DefaultValue, constructed_method.GetParameters()[5].DefaultValue, constructed_method.GetParameters()[6].DefaultValue }; // делаем вызов сконструированного метода, чтобы получить новый набор данных 'tableRows' // IEnumerable<itemType> tableRows = MNLZContext.oracleConnect.Query<itemType>(oracleSelectAllTableRows); // IEnumerable<itemType> tableRows = SqlMapper.Query<itemType>(MNLZContext.oracleConnect, oracleSelectAllTableRows[, null, null, true, null, null]); // поскольку вызывается статический метод, то первый параметр .Invoke() ставим 'null' (объекта нет) object tableRows = constructed_method.Invoke(null, call_parameters); // конструируем тип IEnumerable<itemType> Type ienumerableType = typeof(IEnumerable<>).MakeGenericType(new Type[] { itemType }); // запрашиваем у типа таблицы ObservableCollection<itemType> подходящий конструктор ConstructorInfo constructor = tableType.GetConstructors() .Where(c => c.GetParameters().Length == 1) .Where(c => c.GetParameters()[0].ParameterType.IsEquivalentTo(ienumerableType)) .Single(); // конструируем и возвращаем коллекцию 'new ObservableCollection<itemType>(tableRows)' // получение интерфейса 'IEnumerable<itemType>' от объекта 'tableRows' будет сделано без дополнительных усилий с нашей стороны :) return constructor.Invoke(new object[] { tableRows }); } 

    The table update cycle will look like this:

     foreach (string tableName in dbTables.Keys.ToList()) dbTables[tableName] = getTableRows(tableName); 

    The full text of the program in which I checked the code: https://pastebin.com/8UMW3HXy

    • O. interesting ... there is an error: when a generic method is obtained, the LINQ sample returns empty, the exception “Sequence does not contain elements” falls on the sample by name :-( checked MethodInfo[] generic_methods = MNLZContext.oracleConnect.GetType().GetMethods(); in the test variable MethodInfo[] generic_methods = MNLZContext.oracleConnect.GetType().GetMethods(); - there is simply no method with that name - it seems to me because the Query extension methods that interest us (one returns the result of a hard-typed <T> enumeration, the other is dynamic ) - they both appear on Dapper 'standard OracleConnection How to be? - Alias
    • Well, the parameters of these methods for expanding Query more, 1 is only mandatory, but it just has not come to this yet. - Alias
    • Considering that Dapper's methods extend System.Data.IDbConnection tried to view multiple MethodInfo[] generic_methods = typeof(System.Data.IDbConnection).GetType().GetMethods() , there is also no such method :( - Alias
    • typeof(Dapper.SqlMapper).GetType().GetMethods() similar: (( - Alias
    • @Alias, I'm sorry. I did not know the Dapper library, and was counting on the fact that Query <T> is a class method, not an extension method. Now I realized a mistake, googled and saw what was happening. That method which is caused in your code, found. Look in an hour or two, I think that I can figure out and fix my answer. In any case, thank you for an interesting puzzle :) - velial