Допустим, у меня есть различенный тип объединения AccountEvent
и класс Aggregate
, который имеет два метода:
Apply1(event : AccountEvent)
Apply2(event : Event<AccountEvent>)
Event<'TEvent>
- просто фиктивный класс ради общего типа.
Я пытаюсь создать Expression
, представляющий вызов Apply1
и Apply2
, поддерживающий для типа параметра тип случая Дискриминационное объединение.Это позволяет:
AccountEvent.AccountCreated
тип для Apply1
Event<AccountEvent.AccountCreated>
тип для Apply2
Я хочу добиться этого без без изменения сигнатуры Apply1
, Apply2
и определения дискриминированного объединения.
Код
type AccountCreation = {
Owner: string
AccountId: Guid
CreatedAt: DateTimeOffset
StartingBalance: decimal
}
type Transaction = {
To: Guid
From: Guid
Description: string
Time: DateTimeOffset
Amount: decimal
}
type AccountEvent =
| AccountCreated of AccountCreation
| AccountCredited of Transaction
| AccountDebited of Transaction
type Event<'TEvent>(event : 'TEvent)=
member val Event = event with get
type Aggregate()=
member this.Apply1(event : AccountEvent)=
()
member this.Apply2(event : Event<AccountEvent>)=
()
let createExpression (aggregateType: Type)(eventType: Type)(method: MethodInfo) =
let instance = Expression.Parameter(aggregateType, "a")
let eventParameter = Expression.Parameter(eventType, "e")
let body = Expression.Call(instance, method, eventParameter)
()
[<EntryPoint>]
let main argv =
let accountCreated = AccountEvent.AccountCreated({
Owner = "Khalid Abuhakmeh"
AccountId = Guid.NewGuid()
StartingBalance = 1000m
CreatedAt = DateTimeOffset.UtcNow
})
let accountCreatedType = accountCreated.GetType()
let method1 = typeof<Aggregate>.GetMethods().Single(fun x -> x.Name = "Apply1")
createExpression typeof<Aggregate> typeof<AccountEvent> method1
createExpression typeof<Aggregate> accountCreatedType method1
let method2 = typeof<Aggregate>.GetMethods().Single(fun x -> x.Name = "Apply2")
let eventAccountCreatedType = typedefof<Event<_>>.MakeGenericType(accountCreatedType)
createExpression typeof<Aggregate> typeof<Event<AccountEvent>> method2
createExpression typeof<Aggregate> eventAccountCreatedType method2
0
С моим текущим решением это не работает длясгенерировать выражение для Apply2
:
System.ArgumentException: Expression of type 'Program+Event`1[Program+AccountEvent+AccountCreated]' cannot be used for parameter of type 'Program+Event`1[Program+AccountEvent]' of method 'Void Apply2(Event`1)'
Parameter name: arg0
at at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
at at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression arg0)
at at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
at at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression[] arguments)
at Program.doingStuff(Type aggregateType, Type eventType, MethodInfo method) in C:\Users\eperret\Desktop\ConsoleApp1\ConsoleApp1\Program.fs:40
at Program.main(String[] argv) in C:\Users\eperret\Desktop\ConsoleApp1\ConsoleApp1\Program.fs:61
Мне интересно, как я могу настроить создание своего выражения так, чтобы оно принимало Event<AccountEvent.AccountCreated>
?
Я думаю, что, возможно, существуетнеобходимо иметь промежуточный уровень, чтобы иметь уровень преобразования из AccountEvent.AccountCreated
в его базовый класс AccountEvent
(так компилируются разграниченные объединения), или, точнее, с учетом универсального уровня, от Event<AccountEvent.AccountCreated
до Event<AccountEvent>
.