VB Linq with Prefetch?

Posts   
 
    
Jamison avatar
Jamison
User
Posts: 14
Joined: 22-Dec-2006
# Posted on: 17-Jul-2008 15:01:57   

RESOLVED: Jez describes a fix involving recompiling the Linq support classes and I learn a little bit more about Linq.

I think I'm missing something simple. I have two classic tables Order and OrderLine. I'm trying to retrieve one order and prefetch the order lines.


        Public Function GetOrder(ByVal OrderID As Integer) As OrderzEntity
            Using da As New DataAccessAdapter
                Dim md As New LinqMetaData(da)
                Dim q = (From o In md.Orderz _
                        Where o.OrderId = OrderID _
                        Select o)
                q = q.WithPath(Function(p) p.Prefetch(Of OrderzEntity)(Function(o) o.OrderLine))
                Return q.First
            End Using
        End Function

Two things are happening:

  1. The inference for the Prefetch method doesn't seem to be working right.
  2. I'm getting this error

System.Exception: Expression must be a MemberExpression
at SD.LLBLGen.Pro.LinqSupportClasses.PrefetchPathAPI.PathEdgeRootParser`1.ParseComplexPathSpecification<TDestination>(MemberExpression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.PrefetchPathAPI.PathEdgeRootParser`1.Prefetch<TDestination>(Expression`1 expression)
at AppRiver.ARBO.BLL.Billing.OrderManager._Lambda$__1(IPathEdgeRootParser`1 p) in OrderManager.vb: line 93
at SD.LLBLGen.Pro.LinqSupportClasses.QueryableExtensionMethods.WithPath<TSource>(IQueryable`1 source, Func`2 edgeSpecifierFunc)
at AppRiver.ARBO.BLL.Billing.OrderManager.GetOrder(Int32 OrderID, Boolean throwNotFoundException) in OrderManager.vb: line 93
at AppRiver.ARBO.BLL.Billing.OrderManager.GetOrder(Int32 OrderID) in OrderManager.vb: line 67
at AppRiver.ARBO.BLL.UnitTests.OrderManagerTests.GetOrder() in OrderManagerTests.vb: line 29 

Any ideas on what's wrong?

Thanks, Jamison

Jamison avatar
Jamison
User
Posts: 14
Joined: 22-Dec-2006
# Posted on: 17-Jul-2008 16:27:45   

I am able to use PathEdges to make this work, but I was really hoping "to go linq all-the-way"


                q.WithPath(New PathEdge(Of OrderLineEntity)(OrderzEntity.PrefetchPathOrderLine))


Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 17-Jul-2008 16:59:16   

I think you can also try the following:

        Public Function GetOrder(ByVal OrderID As Integer) As OrderzEntity
            Using da As New DataAccessAdapter
                Dim md As New LinqMetaData(da)
                Dim q = (From o In md.Orderz _
                        Where o.OrderId = OrderID _
                        Select o)
                q.WithPath(Function(p) p.Prefetch(Function(o) o.OrderLine))
                Return q.First
            End Using
        End Function
Jamison avatar
Jamison
User
Posts: 14
Joined: 22-Dec-2006
# Posted on: 17-Jul-2008 17:19:00   

Hi Walaa,

That is how I started (sorry should have said that).


                Dim q = (From o In md.Orderz _
                        Where o.OrderId = OrderID _
                        Select o)
                q.WithPath(Function(p) p.Prefetch(Function(o) o.OrderLine)) 'This is line 91
                Return q.First

Generates this error


System.Exception: Expression must be a MemberExpression
at SD.LLBLGen.Pro.LinqSupportClasses.PrefetchPathAPI.PathEdgeRootParser`1.ParseSimplePathSpecification(MemberExpression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.PrefetchPathAPI.PathEdgeRootParser`1.Prefetch(Expression`1 expression)
at AppRiver.ARBO.BLL.Billing.OrderManager._Lambda$__1(IPathEdgeRootParser`1 p) in OrderManager.vb: line 91
at SD.LLBLGen.Pro.LinqSupportClasses.QueryableExtensionMethods.WithPath<TSource>(IQueryable`1 source, Func`2 edgeSpecifierFunc)
at AppRiver.ARBO.BLL.Billing.OrderManager.GetOrder(Int32 OrderID, Boolean throwNotFoundException) in OrderManager.vb: line 91
at AppRiver.ARBO.BLL.Billing.OrderManager.GetOrder(Int32 OrderID) in OrderManager.vb: line 67
at AppRiver.ARBO.BLL.UnitTests.OrderManagerTests.GetOrder() in OrderManagerTests.vb: line 29 

Jamison

Jez
User
Posts: 198
Joined: 01-May-2006
# Posted on: 17-Jul-2008 19:04:20   

It appears that the VB compiler and the C# compiler generate the expression tree differently. When I originally wrote the lambda prefetch code, I only tested with C# flushed

VB generates UnaryExpressions which wrap the MemberExpressions, while C# just generates the MemberExpressions.

If you have access to the LinqSupportClasses source code, I think the following changes should fix it (I haven't tested it though):


// PathEdgeRootParser.cs line 114
return ParseComplexPathSpecification<TDestination>(expression.Body as MemberExpression);

//PathEdgeRootParser.cs line 125
ParseSimplePathSpecification(expression.Body as MemberExpression);

needs to become...


// PathEdgeRootParser.cs line 114
return ParseComplexPathSpecification<TDestination>(RemoveUnary(expression.Body));

//PathEdgeRootParser.cs line 125
ParseSimplePathSpecification(RemoveUnary(expression.Body));

...and the following method needs to be added to PathEdgeRootParser.cs:


private static MemberExpression RemoveUnary(System.Linq.Expressions.Expression toUnwrap)
{
    if(toUnwrap is UnaryExpression)
    {
        return (MemberExpression)((UnaryExpression)toUnwrap).Operand;
    }

    return toUnwrap as MemberExpression;
}

Jamison avatar
Jamison
User
Posts: 14
Joined: 22-Dec-2006
# Posted on: 17-Jul-2008 19:52:16   

Awesome, Jez! I think that did it. I made your changes, and here is what I am seeing.


            Using da As New DataAccessAdapter
                Dim md As New LinqMetaData(da)
                Dim q = (From o In md.Orderz _
                        Where o.OrderId = OrderID _
                        Select o).WithPath(Function(p) p.Prefetch(Function(o) o.OrderLine))
                Return q.First
            End Using

Prefetch works beautifully. Order and all order lines are retrieved.


            Using da As New DataAccessAdapter
                Dim md As New LinqMetaData(da)
                Dim q = (From o In md.Orderz _
                        Where o.OrderId = OrderID _
                        Select o)
                q.WithPath(Function(p) p.Prefetch(Function(o) o.OrderLine))
                Return q.First
            End Using

Compiles but no prefetch. Order is retrieved, but not order lines.

I'm new to Linq and fairly new to LLBL. Is this the way the code should behave?

Thanks, Jamison

Jez
User
Posts: 198
Joined: 01-May-2006
# Posted on: 17-Jul-2008 20:15:42   

You need to change your second query to overwrite the "q" variable as calling WithPath doesn't modify the existing query (linq expressions are immutable). This should work:


Using da As New DataAccessAdapter
                Dim md As New LinqMetaData(da)
                Dim q = (From o In md.Orderz _
                        Where o.OrderId = OrderID _
                        Select o)
                q = q.WithPath(Function(p) p.Prefetch(Function(o) o.OrderLine))
                Return q.First
End Using

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 17-Jul-2008 20:21:26   

Jez: indeed VB.NET's compiler makes different expression trees sometimes. I'll look into this as well, to see if I can repro it and change the code a bit, based on your suggested changes. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Jez
User
Posts: 198
Joined: 01-May-2006
# Posted on: 17-Jul-2008 20:24:24   

I've attached a patch with the changes.

Attachments
Filename File size Added on Approval
PrefetchPathVBFix.patch 2,970 17-Jul-2008 20:24.37 Approved
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 18-Jul-2008 12:12:44   

Thanks Jez, I'll incorporate it today!

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 18-Jul-2008 14:50:24   

Fixed in next build. simple_smile

Frans Bouma | Lead developer LLBLGen Pro