Esempio n. 1
0
        protected async Task OnReceiveMessageAsync(EntityActorMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (!isInitialized)
            {
                //Only 1 thread ever will call OnInternalReceiveMessageAsync but it prevents
                //any external initialization from happening.
                lock (SyncObj)
                {
                    if (!isInitialized)
                    {
                        if (ExtractPotentialStateMessage(message, out var initMessage))
                        {
                            InitializeState(initMessage.State);

                            //Send successful initialization message to the entity, immediately.
                            //Some entities may not care.
                            OnInitialized(new EntityActorInitializationSuccessMessage());
                        }
                        else
                        {
                            if (Logger.IsWarnEnabled)
                            {
                                Logger.Warn($"{GetType().Name} encountered MessageType: {message.GetType().Name} before INITIALIZATION.");
                            }
                        }

                        //Even if we're initialized now, it's an init message we shouldn't continue with.
                        return;
                    }
                }
            }

            //TODO: Is it safe to capture the Context message ref forever??
            //TODO: Pool or cache somehow.
            EntityActorMessageContext context = new EntityActorMessageContext(Context);

            try
            {
                if (!await HandleMessageAsync(message, context))
                {
                    if (Logger.IsWarnEnabled)
                    {
                        Logger.Warn($"EntityActor encountered unhandled MessageType: {message.GetType().Name}");
                    }
                }
            }
            catch (Exception e)
            {
                if (Logger.IsErrorEnabled)
                {
                    Logger.Error($"Actor: {Self.Path.Address} failed to handle MessageType: {message.GetType().Name} without Exception: {e.Message}\n\nStack: {e.StackTrace}");
                }
                throw;
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Implements must override this and implement domain-specific message handling logic.
        /// </summary>
        /// <param name="message">The message to handle.</param>
        /// <param name="context">The actor message context.</param>
        /// <returns>True if the message was successfully handled.</returns>
        protected Task <bool> HandleMessageAsync(EntityActorMessage message, EntityActorMessageContext context)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            return(MessageHandlerService.HandleMessageAsync(context, message, CancellationToken.None));
        }
        /// <inheritdoc />
        public async Task <bool> HandleMessageAsync(EntityActorMessageContext context, EntityActorMessage message, CancellationToken token = default)
        {
            //We don't lock here even though dictionary is publicly mutable
            //But we discourage calling it.
            if (!MessageHandlerMap.ContainsKey(message.GetType()))
            {
                return(false);
            }

            foreach (var handler in MessageHandlerMap[message.GetType()])
            {
                await handler.HandleMessageAsync(context, message, token);
            }

            return(true);
        }
Esempio n. 4
0
        /// <inheritdoc />
        public override async Task HandleMessageAsync(EntityActorMessageContext context, TMessageRequestType message, CancellationToken token = default)
        {
            //Concept here is to dispatch to the request handler and get a response to send.
            TMessageResponseType response = await HandleRequestAsync(context, message, token);

            //TODO: Validate performance of ordering.
            //Support returning nothing, but ONLY when it's a reference type.
            //Value type responses like 0 or Enum0 or string empty should be considered valid message types.
            if (EqualityComparer <TMessageResponseType> .Default.Equals(response, default) && !typeof(TMessageResponseType).IsValueType)
            {
                return;
            }

            try
            {
                context.Sender.Tell(response, context.Entity);
            }
            finally
            {
                await OnResponseMessageSendAsync(context, message, response);
            }
        }
Esempio n. 5
0
 /// <inheritdoc />
 public override Task HandleMessageAsync(EntityActorMessageContext context, InitializeStateMessage <T> message, CancellationToken token = default)
 {
     //Just initialize the state container.
     MutableStateContainer.Data = message.State;
     return(Task.CompletedTask);
 }
Esempio n. 6
0
 /// <summary>
 /// Implementer can override this method as a callback/event for when the <see cref="response"/> has been sent to the session.
 /// Called after <see cref="HandleMessageAsync"/>.
 ///
 /// Implementers should not await directly within this message as it blocks the request pipeline unless they are absolutely sure they want this to happen.
 /// </summary>
 /// <param name="context">The message context.</param>
 /// <param name="request">The original request message.</param>
 /// <param name="response">The response message sent.</param>
 /// <returns></returns>
 protected virtual Task OnResponseMessageSendAsync(EntityActorMessageContext context, TMessageRequestType request, TMessageResponseType response)
 {
     return(Task.CompletedTask);
 }
Esempio n. 7
0
 /// <summary>
 /// Similar to <see cref="HandleMessageAsync"/> but requires the implementer return an instance of the specified <typeparamref name="TMessageResponseType"/>.
 /// Which will be sent over the network.
 /// </summary>
 /// <param name="context">Message context.</param>
 /// <param name="message">Incoming message.</param>
 /// <param name="token">Cancel token.</param>
 /// <returns></returns>
 protected abstract Task <TMessageResponseType> HandleRequestAsync(EntityActorMessageContext context, TMessageRequestType message, CancellationToken token = default);