Преобразование IQueryable‹EntityObject› в IQueryable‹Specific›

Мы пытаемся преобразовать экземпляр IQueryable<EntityObject> в IQueryable<SpecificEntityObject>, тип SpecificEntityObject известен только во время выполнения.

Мы попытались использовать приведенный ниже код, который не компилируется, потому что тип или пространство имен «objType» не существует.

var t = query.ElementType;
Type objType = typeof(IQueryable<>).MakeGenericType(t);
var typed = query.Cast<IEnumerable<objType>>();


var grouped = typed.GroupByMany(groupBy.Select(grp => grp.Expression).ToArray());

Есть идеи?


person Stuart.Sklinar    schedule 11.01.2012    source источник
comment
По сути, вы не можете сделать это удобно — вам придется использовать отражение, и тогда typed будет object или неуниверсальным IQueryable, и никакие другие вещи не будут работать. Есть вещи, которые вы можете делать здесь, но почти все они будут уродливы, как и все остальное... здесь нет волшебной пули. В качестве примечания, вызов Cast будет <t>, а не <IQueryable<t>> (если вы простите неловкий псевдосинтаксис)   -  person Marc Gravell    schedule 11.01.2012
comment
Что именно вы пытаетесь сделать: привести результат запроса к типу, который вы знаете только во время выполнения, или расширить запрос, чтобы выразить операцию приведения к определенному типу, который вы знаете только во время выполнения?   -  person Paul Turner    schedule 11.01.2012
comment
Почему вы хотите это сделать? Почему вы говорите, что знаете тип только во время выполнения, а затем используете его так же, как знали во время компиляции?   -  person svick    schedule 11.01.2012
comment
Мы пытаемся создать общий API отчетов для нашего приложения. Так что на всех этапах разработки приложений мы никогда не узнаем конкретных типов. У нас есть различные таблицы, но мы сохранили все это в списке «EntityObject», поэтому, когда мы пытаемся сгруппировать вещи, наша GroupBy отражает EntityObject и обнаруживает, что он не имеет требуемых свойств, поскольку базовым типом является EntityObject, а не конкретный тип - сейчас мы находимся в том месте, где мы чувствуем, что наш дизайн может быть неправильным ... есть идеи о том, как динамически GroupBy? - Это вообще имеет смысл?   -  person Stuart.Sklinar    schedule 11.01.2012
comment
Мы отсортировали все это по-другому... мы изменили структуру наших запросов... теперь мы (используя Dynamic Linq) создали новый групповой ключ и передаем его в виде строки, которая анализируется - спасибо за помощь!   -  person Stuart.Sklinar    schedule 17.01.2012


Ответы (4)


Используйте следующий общий метод расширения IQueryable query.ToDTO<sourceType,DestType>();:

public static class QueryableExtensions
{
    public static IQueryable<TDest> ToDTO<TSource, TDest>(this IQueryable<TSource> source)
    {
        List<TDest> destinationList = new List<TDest>();
        List<TSource> sourceList = source.ToList<TSource>();

        var sourceType = typeof(TSource);
        var destType = typeof(TDest);
        foreach (TSource sourceElement in sourceList)
        {
            TDest destElement = Activator.CreateInstance<TDest>();
            //Get all properties from the object 
            PropertyInfo[] sourceProperties = typeof(TSource).GetProperties();
            foreach (PropertyInfo sourceProperty in sourceProperties)
            {
                //and assign value to each propery according to property name.
                PropertyInfo destProperty = destType.GetProperty(sourceProperty.Name);
                destProperty.SetValue(destElement, sourceProperty.GetValue(sourceElement, null), null);
            }
            destinationList.Add(destElement);
        }

        return destinationList.AsQueryable();
    }
}
person Nitin S    schedule 11.01.2012
comment
Хороший код, и я использую его в своем проекте. Одно небольшое замечание: destinationList.Add(destElement); должно быть за скобками. - person Anson Yao; 20.03.2016

Для всех, кто хочет проецировать значения, отличные от БД, из запроса БД, этот проект u/Luis Aguilar был очень и очень полезен для меня.

У меня была очень большая устаревшая база данных (450 ГБ), которую нужно было обслуживать для OData/WebAPI.

Требование OData означало, что я не мог фильтровать исходные данные (сильно), прежде чем вернуть их пользователю. Мы могли бы разрознить их, но, кроме того, это их данные, которые они могут запрашивать по своему усмотрению.

Однако, что еще более важно, устаревшие данные были слишком запутанными, чтобы предоставлять их как есть, и для сопоставления необходимых данных (Include свойств навигации/внешних ключей, длинных предикатов предложений и т. д.) требовалась значительная бизнес-логика.

Это означало, что разбивка на страницы и ограничение результатов не будут доступны до тех пор, пока запрос не будет материализован.

Обычные ярлыки для такого рода вещей включают в себя различные стратегии, включающие материализацию/нетерпеливую загрузку. Однако из-за размера набора данных и отсутствия фильтрации это может привести к массовому раздуванию памяти процесса и сбоям из-за нехватки памяти.

Итак, немного кода. Вот мой вызов конфигурации, аналогичный тому, что требуют AutoMapper или OData:

using ExpressionFramework.Projections;
using ExpressionFramework.Projections.Configuration;

public class ProjectionModelBuilder : ProjectionModel
{
    protected override void OnModelCreating(ProjectionModelBuilder modelBuilder)
    {
        ClientDTO.ProjectionModel(modelBuilder);
        OrderDTO.ProjectionModel(modelBuilder);
        AnotherDTO.ProjectionModel(modelBuilder);
    }
}

Этот дизайн позволяет мне сохранить правила проецирования в классе DTO вместе с остальной бизнес-логикой. Вот как выглядит код уровня DTO:

public static void ProjectionModel(ProjectionModelBuilder modelBuilder)
{
    modelBuilder
        .Projection<ClientDTO>()
        .ForSource<Client>(configuration =>
        {
            configuration.Property(dto => dto.Name).ExtractFrom(entity => entity.Name);
            // etc
        });
}

Где Client — это мой тип Entity/EDM, сопоставленный с таблицей БД и огромным количеством внешних ключей.

Чтобы затем получить переведенный/проецируемый Queryable, вот он:

IClientQueryService service = _ioc.Resolve<IClientQueryService>(); // Repository pattern 
var q = service.GetClients(); // withManyNavigationIncludes
var r = q.Where<Item>(
    i =>
        i.Name != null
        && i.Name != ""
        // lather rinse repeat, with many sub-objects navigated also
    ).AsQueryable();
var projectionModel = new ProjectionModelBuilder();
var s = projectionModel.Project<ClientDTO, Client>(r).AsQueryable();

Только последние две строки имеют значение, но остальные включены для контекста.

Последнее, что мне нужно было сделать, это установить this.IsAutoConfigured = false; в конструкторе для ProjectionSourceTypeConfiguration.cs в коде Луиса; это позволило мне упорядочить определения проекций вручную, чтобы свойства навигации внутри родительских классов успешно настроили их проекции.

Я не могу отблагодарить https://stackoverflow.com/users/543712/luis-aguilar за его работу . После того, как я написал свой собственный LINQ Provider/ExpressionVisitor с различными вызовами универсальных методов, переводами и проходами по дереву, чтобы все еще иметь различные проблемы, его проект был удачей.

Если вы обнаружите, что должны конвейеризировать обработку собственных выражений для повышения производительности или по другим причинам, я бы рекомендовал эти два ответа для начала.

person user326608    schedule 08.12.2015

Если вы начали использовать отражение, вам нужно использовать его и со всеми методами. Итак, вам нужно создать

var myEnumType = typeof(IEnumerable<>).MakeGenericType(objType);

а также найти метод расширения Cast, соответствующий нужному типу, также во время выполнения.

 myEnumType.GetMethod("Cast", BindingFlags.Public |
                BindingFlags.Static, 
                null, 
                CallingConventions.Any,  
                new Type[] {typeof(object)}, 
                null);

тогда вы сможете вызвать этот метод

person Sasha    schedule 11.01.2012

person    schedule
comment
Привет, добро пожаловать в SO. Не могли бы вы дать какое-то объяснение вашего решения? - person GraphicsMuncher; 06.03.2014