Не удается найти свойство или поле в динамическом объекте

Я использовал 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()
Другие вопросы по тегам