Click here to Skip to main content
15,887,346 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have a piece of code that is supposed to use Linq Expressions to put together a "compiled at run time" lambda function to persist events to a stream.

(This is to allow the function to be swapped out by injection if you want to use a different serialisation technology)

The code is:-

VB
Public Function CreateDefaultSaveToStreamFunction(Of TEvent As {New, IEvent})() As Func(Of TEvent, System.IO.Stream, Long)

       'otherwise build the function by reflection
       Dim valueParameter As ParameterExpression =
           Expression.Parameter(GetType(TEvent), "eventToSerialise")

       'Type to be populated...
       Dim streamParameter As ParameterExpression =
           Expression.Parameter(GetType(System.IO.Stream), "stream")


       Dim sequenceParameter As ParameterExpression =
           Expression.Variable(GetType(Long), "sequence")

       Dim innerBlockExpressions As New List(Of Expression)()

       'Dim sequence As Long

       innerBlockExpressions.Add(Expression.Assign(sequenceParameter,
               Expression.[New](GetType(Long))))

       'Dim bf As New BinaryFormatter()
       Dim binaryFormatterParameter As ParameterExpression =
           Expression.Variable(GetType(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter), "bf")

       innerBlockExpressions.Add(Expression.Assign(binaryFormatterParameter,
               Expression.[New](GetType(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter))))

       'Public Sub Serialize(serializationStream As Stream, graph As Object)
       Dim serialiseMethod As MethodInfo = GetType(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter).GetMethod("Serialize", {GetType(System.IO.Stream), GetType(Object)})

       innerBlockExpressions.Add(
                                   Expression.Call(binaryFormatterParameter, serialiseMethod, {streamParameter, valueParameter})
                                 )

       'sequence = streamParameter.Position
       innerBlockExpressions.Add(Expression.Assign(sequenceParameter,
                                                   Expression.PropertyOrField(streamParameter, "Position")
                                                   )
                                 )

       'Return sequenceParameter
       innerBlockExpressions.Add(sequenceParameter)

       Dim innerBlock As BlockExpression = Expression.Block({valueParameter, streamParameter, sequenceParameter, binaryFormatterParameter},
                                                            innerBlockExpressions.AsEnumerable())

       Dim toStreamBody As BlockExpression = Expression.Block(
           New ParameterExpression() {valueParameter, streamParameter},
           innerBlock)

       Dim retLambda As LambdaExpression = Expressions.Expression(Of EventSerializer(Of TEvent).SaveToStream).Lambda(toStreamBody, {valueParameter, streamParameter})
       Dim retDeletage = retLambda.Compile()

       Return retDeletage

   End Function


When I run this for a given event type it creates a lambda expression like:-
.Block(
    CQRSAzure.EventSourcing.UnitTest.Mocking.MockEventTypeTwo $eventToSerialise,
    System.IO.Stream $stream) {
    .Block(
        CQRSAzure.EventSourcing.UnitTest.Mocking.MockEventTypeTwo $eventToSerialise,
        System.IO.Stream $stream,
        System.Int64 $sequence,
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter $bf) {
        $sequence = .New System.Int64();
        $bf = .New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        .Call $bf.Serialize(
            $stream,
            $eventToSerialise);
        $sequence = $stream.Position;
        $sequence
    }
}


When I call this Lambda function it does return the stream position (in the variable "sequence") but the stream itself is not written back to the variable I pass in.

What I have tried:

Tried changing around the method signature

{Method = {Int64 lambda_method(System.Runtime.CompilerServices.Closure, CQRSAzure.EventSourcing.UnitTest.Mocking.MockEventTypeTwo, System.IO.Stream)}}


This shows the stream is being passed in - and the length is also being set..?
Posted
Updated 2-Dec-17 9:56am

1 solution

Ah - evidently a parameter for a lambda (except the last parameter) must be an In parameter - which does make sense from the point of view of marshalling ... hmm, so evidently this cannot be used to serialise to a passed-in stream.
 
Share this answer
 
Comments
Richard Deeming 5-Dec-17 12:10pm    
That doesn't sound right. It's perfectly possible to use ByRef parameters, so long as you use a custom delegate type.

What is the generated method supposed to look like?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900