Динамическое создание типа во время выполнения для метода множественного чтения dapper querymultiple с использованием отражения

#reflection #types #dapper

#отражение #типы #dapper

Вопрос:

Я пытаюсь динамически передавать тип класса во время выполнения. Приведенный ниже код выдает ошибку в разделе code:

     object newObject Activator.CreateInstance(Type.GetType(t.GetGenericArguments()[0].FullName));
    data.Read<newObject>();
  

Я также пытался

     data.Read<Type.GetType(t.GetGenericArguments()[0].FullName)>();  
  

Вот полный метод:

     public object FetchMultipleRecordSet(string storedProcedure, IList<QueryParameter> parameterCollection, object dataList)
    {
        if (!string.IsNullOrEmpty(storedProcedure))
        {
            using (SqlConnection sql = CreateDatabaseConnection())
            {
                DynamicParameters dynamicParameter = ConvertToDynamicParameters(parameterCollection);
                var data = sql.QueryMultiple(storedProcedure, dynamicParameter, null, null, commandType: CommandType.StoredProcedure);

                PropertyInfo[] properties = dataList.GetType().GetProperties();
                foreach (PropertyInfo property in properties)
                {
                    Type t = property.PropertyType;
                    if (t.BaseType == null amp;amp; t.IsGenericType amp;amp; t.Namespace == "System.Collection.Generic")
                    {
                        //property.SetValue(data.Read());
                        object newObject = Activator.CreateInstance(Type.GetType(t.GetGenericArguments()[0].FullName));
                        data.Read<>();    
                    }
                    else if (t.Namespace != "System")
                    {
                        //typeCollection.Add(Type.GetType(t.FullName));
                    }
                }
            }
        }

        return dataList;
    }
  

Вот модель представления, которую я хочу использовать:

     public class ResultCollection
    {
        public IList<ShortCodeList> ShortCodeListCollection { get; set; }
        public DateTime CurrentDate { get; set; }
        public UserMembershipPlan UserMembershipPlanRecord { get; set; }
        public IList<EmailRecipients> EmailRecipientsCollection { get; set; }
    }
  

Мне нужно передать тип в data.Read(), чтобы свойство, которое находится в универсальной форме, могло быть сопоставлено с результирующим набором. Если я передам «newObject» или «Type.GetType(t.GetGenericArguments()[0].FullName)» по-прежнему выдает ошибку. Это может показаться неуклюжим, но я думаю, что это должно сработать.

Ответ №1:

На данный момент типизированный API для dapper использует общие. Существует нетипизированный API, но вам нужно будет выполнить собственное сопоставление элементов. Чтобы вызвать универсальный метод через тип, вам нужно использовать MethodInfo для универсального метода, MakeGenericMethod и Invoke. Существует также способ подделать его с помощью dynamic , добавив в ваш код метод shim, похожий на:

 dynamic template = ...  // activator etc
Evil(template, otherArgs...);

Evil<T>(T template, otherArgs...) {
    use some <T> method etc here
}
  

В качестве более полного примера работает следующее:

 public void TypeBasedViaDynamic()
{
    Type type = GetSomeType();

    dynamic template = Activator.CreateInstance(type);
    dynamic actual = CheetViaDynamic(template,
        "select @A as [A], @B as [B]", new { A = 123, B = "abc" });
    ((object)actual).GetType().IsEqualTo(type);
    int a = actual.A;
    string b = actual.B;
    a.IsEqualTo(123);
    b.IsEqualTo("abc");
}

T CheetViaDynamic<T>(T template, string query, object args)
{
    return connection.Query<T>(query, args).SingleOrDefault();
}
static Type GetSomeType()
{
    return typeof(SomeType);
}
public class SomeType
{
    public int A { get;set; }
    public string B { get;set; }
}
  

Обратите внимание, что это начинается просто с Type и заполняет экземпляр через фиктивный экземпляр (через Activator ) и dynamic . Не очень красиво, но это работает. Однако я также просто внес некоторые изменения в dapper, которые принимают Type экземпляр в качестве параметра, благодаря чему следующее также работает — без необходимости в этих взломах:

 public void TypeBasedViaType()
{
    Type type = GetSomeType();

    dynamic actual = connection.Query(type,
        "select @A as [A], @B as [B]", new { A = 123, B = "abc" }
        ).FirstOrDefault();
    ((object)actual).GetType().IsEqualTo(type);
    int a = actual.A;
    string b = actual.B;
    a.IsEqualTo(123);
    b.IsEqualTo("abc");
}
  

И многосеточных считывателей тоже:

 public void TypeBasedViaTypeMulti()
{
    Type type = GetSomeType();

    dynamic first, second;
    using(var multi = connection.QueryMultiple(
        "select @A as [A], @B as [B]; select @C as [A], @D as [B]",
        new { A = 123, B = "abc", C = 456, D = "def" }))
    {
        first = multi.Read(type).Single();
        second = multi.Read(type).Single();
    }
    ((object)first).GetType().IsEqualTo(type);
    int a = first.A;
    string b = first.B;
    a.IsEqualTo(123);
    b.IsEqualTo("abc");

    ((object)second).GetType().IsEqualTo(type);
    a = second.A;
    b = second.B;
    a.IsEqualTo(456);
    b.IsEqualTo("def");
}
  

Обратите внимание, что dynamic в этих новых примерах API это сделано исключительно для удобства тестирования; dynamic не является важной частью подхода.

Комментарии:

1. Привет, Марк, спасибо, что ответил на вопрос. Я начал работать над какой-то другой концепцией. Было бы здорово, если бы вы могли ввести меня в ту часть, которую вы добавили сюда. Я почти потерял надежду на какой-либо ответ отсюда.

2. @Deepak был на мобильном телефоне все выходные — добавит пример, когда на ПК