Ошибка при преобразовании типа Dynami c - объект должен реализовывать IConvertible - PullRequest
1 голос
/ 18 июня 2020

Я пытаюсь написать деревья выражений для динамического построения LINQ. У меня есть сетка на стороне клиента с отложенной загрузкой данных сетки на стороне сервера. Сетка запрашивает строки, которые включают модель фильтра в ресурс запроса. Данное свойство моего ресурса запроса может иметь несколько типов в зависимости от того, какой столбец фильтруется.

Я искал SO и нашел несколько похожих сообщений, но они не совсем охватывали мою точную настройку / проблемы - поскольку большинство ответов было больше сосредоточено на использовании Convert.ChangeType или использовании dynamic, оба из которых я уже использую.

Вот FilterModel:

public class FilterModel
{
    public string Type { get; set; }
    public IEnumerable<dynamic> Values { get; set; }
}

Итак Вот некоторые примеры запросов:

Фильтр по набору идентификаторов:

{
    type: "set"
    values: [1, 2, 3, 4]
}

Фильтр по набору категорий:

{
    type: "set"
    values: ["Category_1", "Category_3"]
}

где тип каждой записи в values определяется типом свойства соответствующего столбца объекта-строки.

Ошибка и код:

Я получаю сообщение об ошибке при попытке для преобразования в тип свойства столбца объекта Row: System.InvalidCastException: Object must implement IConvertible.

Настройка:

FilterModel filter = new FilterModel() { Type = "set", Values = [1, 2, 3, 4] };

ParameterExpression rowModelParameterExpression = Expression.Parameter(typeof(RowModel));

string propertyName = "id"; // "category"
PropertyInfo property = typeof(RowModel).GetProperty(propertyName);
Type propertyType = property.PropertyType;

MemberExpression propertyExpression = Expression.Property(rowModelParameterExpression , property);

Преобразование logi c (ошибка в этом коде) :

Type listType = typeof(List<>).MakeGenericType(new[] { propertyType });
MethodInfo contains = listType.GetMethod("Contains", new[] { propertyType });
Expression comparisonValue = Expression.Constant(Convert.ChangeType(filter.Values, listType));

Expression predicateBody = Expression.Call(comparisonValue, contains, propertyExpression);

1 Ответ

1 голос
/ 18 июня 2020

Вы не можете напрямую преобразовать / преобразовать IEnumerable<dynamic> в List<sometype>. Я предлагаю вам преобразовать его в массив и использовать метод Enumerable.Contains<T> для предиката. Примерно так:

FilterModel filter = new FilterModel() { Type = "set", Values = new dynamic[] { 1, 2, 3, 4 } };
ParameterExpression rowModelParameterExpression = Expression.Parameter(typeof(RowModel));

string propertyName = "id"; // "category"
PropertyInfo property = typeof(RowModel).GetProperty(propertyName);
Type propertyType = property.PropertyType;

MemberExpression propertyExpression = Expression.Property(rowModelParameterExpression, property);

MethodInfo contains = typeof(Enumerable).GetMethods()
    .Where(mi => mi.Name == "Contains" && mi.GetParameters().Length == 2)
    .Single()
    .MakeGenericMethod(new[] { propertyType });


var values = filter.Values.Select(i => Convert.ChangeType(i, propertyType)).ToArray();
var arr = Array.CreateInstance(propertyType, values.Length);
Array.Copy(values, arr, values.Length);

Expression comparisonValue = Expression.Constant(arr);

Expression predicateBody = Expression.Call(null, contains, comparisonValue, propertyExpression);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...