Note: I purposefully simplified the design below
Let's say, I have a discriminated union type AccountEvent
and a class Aggregate
that carries two methods:
Apply1(event : AccountEvent)
Apply2(event : Event<AccountEvent>)
Event<'TEvent>
being just a dummy class for sake of having a generic type.
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 doingStuff (aggregateType: Type)(eventType: Type)(method: MethodInfo) =
let aggregateParameter = Expression.Parameter(aggregateType, "a")
let eventParameter = Expression.Parameter(eventType, "e")
let body = Expression.Call(aggregateParameter, 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")
doingStuff typeof<Aggregate> typeof<AccountEvent> method1
doingStuff typeof<Aggregate> accountCreatedType method1
let method2 = typeof<Aggregate>.GetMethods().Single(fun x -> x.Name = "Apply2")
let eventAccountCreatedType = typedefof<Event<_>>.MakeGenericType(accountCreatedType)
doingStuff typeof<Aggregate> typeof<Event<AccountEvent>> method2
doingStuff typeof<Aggregate> eventAccountCreatedType method2
0
What I am trying to do it's to create an Expression
corresponding to the call of Apply1
and Apply2
with the type of a discriminated union case as one of the parameter type.
In the case of Apply1
if I am building an Expression
be it with AccountEvent
or the runtime type of AccountEvent.AccountCreated
. It works
However, if I want to achieve the same with Apply2
while it works with Event<AccountEvent>
, but it fails working with Event<AccountEvent.AccountCreated>
:
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
I am wondering how I can adjust the creation of my expression to accept the Event<AccountEvent.AccountCreated>
?
I am thinking that maybe there is a need to have an intermediate layer to have a conversion layer from AccountEvent.AccountCreated
to its base classAccountEvent
(this is how discriminated unions are compiled), or more precisely considering the generic, from Event<AccountEvent.AccountCreated
to Event<AccountEvent>
.
Aucun commentaire:
Enregistrer un commentaire