IQueryableEntityObject> is converted to IQueryableSpecific>.

c# casting entity-framework iqueryable linq

Question

We are trying to cast an instance of IQueryable<EntityObject> to an IQueryable<SpecificEntityObject>, the SpecificEntityObject type is only known at runtime.

We have tried using the code below, which does not compile because The type or namespace 'objType' does not exist.

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());

Any ideas?

1
11
1/11/2012 12:08:21 PM

Accepted Answer

Use following IQueryable extension generic method 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();
    }
}
7
5/16/2019 4:10:42 AM

Popular Answer

For anyone else wanting to to project non-db values from a db query, this project from u/Luis Aguilar was very, very helpful to me.

I had a very large legacy database (450GB) which was required to be served to OData/WebAPI.

The OData requirement meant I could not filter the source data (much) before returning it to the user. We could silo it, but apart from that it is their data to query as they wish.

More importantly, however, the legacy data was far too convoluted to expose as-is, and there was significant business logic required to collate the necessary data (Include of navigation properties/foreign keys, lengthy clause predicates, etc).

This meant the pagination and result limiting would not be available until after the query was already materialized.

Normal shortcuts for this kind of thing involve various strategies that involve materialization/eager loading. However, due to the size of the dataset and lack of filtering, this would result in massive process memory bloat and out-of-memory crashes.

So, some code. Here's my config call, similar to what AutoMapper or OData require:

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);
    }
}

This design allows me to keep the projection rules in the DTO class with the rest of the business logic. Here's what the DTO-level code looks like:

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

Where Client is my Entity/EDM type, mapped to db table and a gazillion foreign keys.

To then get a translated/projected Queryable, this is it:

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();

Only the last two lines are relevant, but included the rest for context.

The last thing I had to do was set this.IsAutoConfigured = false; in the constructor for ProjectionSourceTypeConfiguration.cs in Luis' code; this allowed me to order my projection definitions manually so navigation properties inside parent classes would configure their projections successfully.

I can't thank https://stackoverflow.com/users/543712/luis-aguilar enough for his work. After writing my own LINQ Provider/ExpressionVisitor with various generic method invocations, translations and treewalks to still have various problems, his project was a godsend.

If you do find to have to pipeline your own expression processing for performance or other reasons, I'd recommend these two answers to begin with.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow