Не удается найти свойство или поле в динамическом объекте
Я использовал Dynamic Linq для выполнения некоторых запросов к базе данных, и до сих пор он работает очень хорошо. Я могу передать строку Select
чтобы выбрать поля так:
var output = myDatabaseTable.Select("Foo, Bar");
Например. Сила, очевидно, заключается в том, что вы передаете строковую переменную, а не жестко закодированные строки. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что библиотека использует IEnumerable
вместо IEnumerable<T>
потому что, очевидно, он не может знать T
до времени выполнения. Я использую это, чтобы выбрать данные и в конечном итоге вернуть их клиенту, и он отлично работает для выплескивания необработанных данных, но теперь я хочу иметь возможность выполнить еще некоторую обработку перед возвратом данных, и для этого требуется заставить запрос выполняться база данных в первую очередь. Я могу сделать что-то вроде этого:
var materializedResults = output.Cast<dynamic>().ToList();
И это сделает запрос выполненным. Но проблема в том, что, как только я это сделаю, кажется, я больше не могу использовать динамический linq. Например, если я сделал что-то вроде этого:
var foos = materializedResults.Select("Foo");
Теперь я получаю System.Linq.Dynamic.ParseException
с сообщением No property of field 'Foo' exists in type 'Object'
(Примечание: в отладчике видно, что materializedResults
действительно имеет все ожидаемые свойства).
Так что после приведения к List
так что я могу потенциально перебирать его и изменять некоторые значения, я больше не могу запрашивать его.
Поэтому мой вопрос заключается в том, как я могу взять динамический запрос (с указанием select, group by, order by и т. Д. В виде строк), материализовать результаты и затем фактически обработать эти результаты динамически?
Я подумал, может быть, если бы я мог привести к фактическому типу, а не dynamic
это может сработать, поэтому я попробовал это:
var d = output.Cast<dynamic>().ToList();
MethodInfo method = typeof(Enumerable).GetMethod("Cast", new[] {typeof(IEnumerable)});
method = method.MakeGenericMethod(d.First().GetType());
output = method.Invoke(d, new[] {d}) as IEnumerable;
Что некрасиво и требует от меня двойного применения. Первый раз dynamic
так что я могу получить тип из первого элемента, а затем снова к этому типу.
2 ответа
Если вы делаете YourStuff.Cast<dynamic>.ToList()
, вы получите IEnumerable<object>
и нет собственности Foo
по типу object
,
Вопрос, который вы можете задавать, как вы можете получить IList<TheActualType>
?! Вы можете сделать это следующим образом:
// for IEnumerable
public static IList ToAnonymousList(this IEnumerable enumerable)
{
var enumerator = enumerable.GetEnumerator();
if (!enumerator.MoveNext())
throw new Exception("?? No elements??");
var value = enumerator.Current;
var returnList = (IList) typeof (List<>)
.MakeGenericType(value.GetType())
.GetConstructor(Type.EmptyTypes)
.Invoke(null);
returnList.Add(value);
while (enumerator.MoveNext())
returnList.Add(enumerator.Current);
return returnList;
}
// for IQueryable
public static IList ToAnonymousList(this IQueryable source)
{
if (source == null) throw new ArgumentNullException("source");
var returnList = (IList) typeof (List<>)
.MakeGenericType(source.ElementType)
.GetConstructor(Type.EmptyTypes)
.Invoke(null);
foreach (var elem in source)
returnList.Add(elem);
return returnList;
}
Это простой метод расширения, который впоследствии можно использовать как таковой:
var test = (new[]
{
new
{
Property1 = "10",
Property2 = "10",
Property3 = 1
}
}
.Select("New(Property1, Property2)"))
.ToAnonymousList();
Ваш бросок по умолчанию, который вызовет ошибку
(output as System.Collections.Generics.IEnumerable)
Это приведение определило правильный интерфейс Попробуйте еще раз
(output as System.Collections.IEnumerable).Cast<dynamic>().ToList()