Duncan Edwards Jones Ответов: 1

Передайте system.io.stream по ссылке на expression.parameter


У меня есть фрагмент кода, который должен использовать выражения Linq для создания "скомпилированной во время выполнения" лямбда-функции для сохранения событий в потоке.

(Это делается для того, чтобы функция могла быть заменена инъекцией, если вы хотите использовать другую технологию сериализации)

Код таков:-

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


Когда я запускаю это для данного типа события, оно создает лямбда-выражение, например:-
.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
    }
}


Когда я вызываю эту лямбда-функцию, она возвращает позицию потока (в переменной "последовательность"), но сам поток не записывается обратно в переменную, которую я передаю.

Что я уже пробовал:

Попробовал изменить сигнатуру метода

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


Это показывает, что поток передается - и длина также устанавливается..?

1 Ответов

Рейтинг:
2

Duncan Edwards Jones

Ах - очевидно, параметр для лямбды (за исключением последнего параметра) должен быть параметром In - что действительно имеет смысл с точки зрения маршалинга ... хм, Так что, очевидно, это не может быть использовано для сериализации в передаваемый поток.


Richard Deeming

Что-то тут не так. Его вполне можно использовать ByRef параметры, если вы используете пользовательский тип делегата.

Как должен выглядеть сгенерированный метод?