/// <summary> /// Initializes a new instance. /// </summary> /// <param name="name"></param> /// <param name="filterExpression"></param> /// <param name="itemsExpression"></param> /// <param name="requestIdExpression"></param> /// <param name="accessor"></param> /// <param name="settings"></param> public StateMachineMultiRequest( string name, Expression <Func <TInstance, Guid, bool> > filterExpression, Expression <Func <TInstance, IEnumerable <TState> > > itemsExpression, Expression <Func <TState, Guid?> > requestIdExpression, IMultiRequestStateAccessor <TInstance, TState, TRequest, TResponse> accessor, MultiRequestSettings settings) { this.name = name ?? throw new ArgumentNullException(nameof(name)); this.filterFunc = filterExpression?.Compile() ?? throw new ArgumentNullException(nameof(filterExpression)); this.itemsFunc = itemsExpression?.Compile() ?? throw new ArgumentNullException(nameof(itemsExpression)); this.requestIdFunc = requestIdExpression?.Compile() ?? throw new ArgumentNullException(nameof(requestIdExpression)); this.accessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); this.settings = settings; }
/// <summary> /// Declares a request that is sent by the state machine to a service, and the associated response, fault, and /// timeout handling. The property is initialized with the fully built Request. The request must be declared /// before it is used in the state/event declaration statements. /// </summary> /// <typeparam name="TState"></typeparam> /// <typeparam name="TRequest"></typeparam> /// <typeparam name="TResponse"></typeparam> /// <param name="propertyExpression"></param> /// <param name="itemsExpression"></param> /// <param name="requestIdExpression"></param> /// <param name="accessor"></param> /// <param name="settings"></param> protected void MultiRequest <TState, TRequest, TResponse>( Expression <Func <MultiRequest <TInstance, TState, TRequest, TResponse> > > propertyExpression, Expression <Func <TInstance, IEnumerable <TState> > > itemsExpression, Expression <Func <TState, Guid?> > requestIdExpression, IMultiRequestStateAccessor <TInstance, TState, TRequest, TResponse> accessor, MultiRequestSettings settings = null) where TRequest : class where TResponse : class { // default settings if (settings == null) { settings = new StateMachineMultiRequestConfigurator <TRequest>(); } var property = propertyExpression.GetPropertyInfo(); // parameters reused within expressions var instanceParameter = Expression.Parameter(typeof(TInstance), "instance"); var itemParameter = Expression.Parameter(typeof(TState), "item"); var requestIdParameter = Expression.Parameter(typeof(Guid), "requestId"); // filters an instance to that which contains the request var filterExpression = Expression.Lambda <Func <TInstance, Guid, bool> >( Expression.Call( typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(TState) }, itemsExpression.Body.Replace(itemsExpression.Parameters[0], instanceParameter), Expression.Lambda <Func <TState, bool> >( Expression.Equal( Expression.Convert(requestIdExpression.Body.Replace(requestIdExpression.Parameters[0], itemParameter), typeof(Guid?)), Expression.Convert(requestIdParameter, typeof(Guid?))), itemParameter)), instanceParameter, requestIdParameter); var request = new StateMachineMultiRequest <TInstance, TState, TRequest, TResponse>(property.Name, filterExpression, itemsExpression, requestIdExpression, accessor, settings); InitializeMultiRequest(this, property, request); Event(propertyExpression, x => x.Finished); // filters instances for the Completed event var completedContextParameter = Expression.Parameter(typeof(ConsumeContext <TResponse>), "context"); var completedExpression = Expression.Lambda <Func <TInstance, ConsumeContext <TResponse>, bool> >( Expression.Call( typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(TState) }, itemsExpression.Body.Replace(itemsExpression.Parameters[0], instanceParameter), Expression.Lambda <Func <TState, bool> >( Expression.Equal( requestIdExpression.Body.Replace(requestIdExpression.Parameters[0], itemParameter), Expression.Property(completedContextParameter, typeof(MessageContext), nameof(MessageContext.RequestId))), itemParameter)), instanceParameter, completedContextParameter); // filters instances for the Faulted event var faultedContextParameter = Expression.Parameter(typeof(ConsumeContext <Fault <TRequest> >), "context"); var faultedExpression = Expression.Lambda <Func <TInstance, ConsumeContext <Fault <TRequest> >, bool> >( Expression.Call( typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(TState) }, itemsExpression.Body.Replace(itemsExpression.Parameters[0], instanceParameter), Expression.Lambda <Func <TState, bool> >( Expression.Equal( requestIdExpression.Body.Replace(requestIdExpression.Parameters[0], itemParameter), Expression.Property(faultedContextParameter, typeof(MessageContext), nameof(MessageContext.RequestId))), itemParameter)), instanceParameter, faultedContextParameter); // filters instances for the TimeoutExpired event var timeoutExpiredContextParameter = Expression.Parameter(typeof(ConsumeContext <RequestTimeoutExpired <TRequest> >), "context"); var timeoutExpiredExpression = Expression.Lambda <Func <TInstance, ConsumeContext <RequestTimeoutExpired <TRequest> >, bool> >( Expression.Call( typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(TState) }, itemsExpression.Body.Replace(itemsExpression.Parameters[0], instanceParameter), Expression.Lambda <Func <TState, bool> >( Expression.Equal( requestIdExpression.Body.Replace(requestIdExpression.Parameters[0], itemParameter), Expression.Convert( Expression.Property( Expression.Property(timeoutExpiredContextParameter, typeof(ConsumeContext <RequestTimeoutExpired <TRequest> >), nameof(ConsumeContext <RequestTimeoutExpired <TRequest> > .Message)), typeof(RequestTimeoutExpired <TRequest>), nameof(RequestTimeoutExpired <TRequest> .RequestId)), typeof(Guid?))), itemParameter)), instanceParameter, timeoutExpiredContextParameter); Event(propertyExpression, x => x.Completed, x => x.CorrelateBy(completedExpression)); Event(propertyExpression, x => x.Faulted, x => x.CorrelateBy(faultedExpression)); Event(propertyExpression, x => x.TimeoutExpired, x => x.CorrelateBy(timeoutExpiredExpression)); Event(propertyExpression, x => x.FinishedSignal, x => x.CorrelateById(m => (Guid)m.CorrelationId)); State(propertyExpression, x => x.Pending); DuringAny( When(request.Completed, request.CompletedEventFilter) .Add(new MultiRequestItemCompletedActivity <TInstance, TState, TRequest, TResponse>(request)) .Add(new MultiRequestItemFinishedActivity <TInstance, TState, TRequest, TResponse>(request)) .Add(new MultiRequestCancelItemTimeoutActivity <TInstance, TState, TRequest, TResponse>(request)), When(request.Faulted, request.FaultedEventFilter) .Add(new MultiRequestItemFaultedActivity <TInstance, TState, TRequest, TResponse>(request)) .Add(new MultiRequestItemFinishedActivity <TInstance, TState, TRequest, TResponse>(request)) .Add(new MultiRequestCancelItemTimeoutActivity <TInstance, TState, TRequest, TResponse>(request)), When(request.TimeoutExpired, request.RequestTimeoutExpiredEventFilter) .Add(new MultiRequestItemTimeoutExpiredActivity <TInstance, TState, TRequest, TResponse>(request)) .Add(new MultiRequestItemFinishedActivity <TInstance, TState, TRequest, TResponse>(request)) .Add(new MultiRequestCancelItemTimeoutActivity <TInstance, TState, TRequest, TResponse>(request)), When(request.FinishedSignal, request.FinishedSignalEventFilter) .Add(new MultiRequestFinishedActivity <TInstance, TState, TRequest, TResponse>(request))); }