Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast IQueryable<EntityObject> to IQueryable<Specific>

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?

like image 282
Stuart.Sklinar Avatar asked Jan 11 '12 12:01

Stuart.Sklinar


People also ask

What inherits from IQueryable?

The IQueryable interface inherits the IEnumerable interface so that if it represents a query, the results of that query can be enumerated. Enumeration causes the expression tree associated with an IQueryable object to be executed. The definition of "executing an expression tree" is specific to a query provider.

What is the difference between returning IQueryable T vs IEnumerable T >?

Both IEnumerable and IQueryable are forward collection. Querying data from a database, IEnumerable execute a select query on the server side, load data in-memory on a client-side and then filter data. Querying data from a database, IQueryable execute the select query on the server side with all filters.

What is difference between IQueryable and list in C#?

IQueryable is useful when we want to iterate a collection of objects which deals with ad-hoc queries against the data source or remote database, like SQL Server. IList is useful when we want to perform any operation like Add, Remove or Get item at specific index position in the collection.

What is AsQueryable C#?

AsQueryable() in C# AsQueryable() method is used to get an IQueryable reference. Let us see an example to find sum of integer values. Firstly, set an integer array. var arr = new int[] { 100, 200, 300, 400 };


2 Answers

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();
    }
}
like image 127
Nitin S Avatar answered Sep 25 '22 16:09

Nitin S


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.

like image 30
user326608 Avatar answered Sep 22 '22 16:09

user326608