Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute expression on another IQueryable source

There are two classes with identical field structure: WebMessage and WebMessageDto.

I have a DataGrid which can work with both classes. And I have a RIA Service which can provide only WebMessageDto because WebMessage has navigation properties and they cannot be serialized.

Now, DataGrid should query RIA Service method returning IQueryable<WebMessageDto>. I need handle DataGrid's query and execute it on ORM's Table<WebMessage>, then transform result to WebMessageDto type and return it to DataGrid.

I started to implement custom IQueryable interface and I can handle expression.

public class WebMessageQueryContext
{
    // Executes the expression tree that is passed to it. 
    internal static object Execute(Expression expression, bool isEnumerable)
    {
        // DataContext.WebMessages is a ORM table which returns IQueryable<WebMessage>
        List<WebMessage> list = DataContext.WebMessages.Provider.Execute(expression);

        return WebMessageDto.ConvertFrom(list); // returning List<WebMessageDto>
    }
}

The code above will make recursive calls to this method. And I found microsoft's sample where queryable source changes in ExpressionVisitor and now I change original source to ORM's table.

    protected override Expression VisitConstant(ConstantExpression c)
    {
        if (c.Type == typeof(WebMessageDtoQuerySource<WebMessageDto>))
            return Expression.Constant(DataContext.WebMessages);

        return c;
    }

I'm getting exception when I execute the expression:

Expression of type 'System.Data.Linq.Table'1[WebMessage]' cannot be used for parameter of type 'System.Linq.IQueryable'1[WebMessageDto]' of method 'Int32 Count[WebMessageDto](System.Linq.IQueryable1[WebMessageDto])'`

There's no much information about IQueryableProviders and I don't know what to do... Who can answer, can I do it theoretically?

like image 332
opewix Avatar asked Jun 20 '14 11:06

opewix


1 Answers

Sorry guys, I was wrong. Before question edit I wrote that Select applied to IQueryable before Where causes loading all table rows in memory and then executing Where statement. But actually such behavior was caused by incorrect implementation of custom linq provider.

When I used datagrid without custom linq provider I mentioned that Select executes after Where even when Select is applied before Where.

In my code:

public IQueryable<WebMessageDto> Dtos 
{ 
    get { return db.WebMessages.Select(r => new WebMessageDto { Id = r.Id });} 
}

My DataGrid queries this method with Where/OrderBy/GroupBy statements and conversion to WebMessageDto occurs after them.

So, Alex Key's answer is also applicable.

like image 194
opewix Avatar answered Sep 30 '22 14:09

opewix