Inheritance: Message
        async Task OnRoomJoin(JoinRoom msg, IOperationContext context)
        {
            var newMember = new RoomMember
            {
                RoomName = msg.RoomName,
                UserName = msg.ClientName,
                UserChannel = context.ReplyChannel
            };

            var newEvent = new RoomEvent() { UserName = msg.ClientName, Text = "Joined" };

            RoomData room;
            if (!Rooms.TryGetValue(msg.RoomName, out room))
            {
                room = new RoomData(msg.RoomName);
                room.Members = new List<RoomMember> { newMember };
                
                Rooms.Add(msg.RoomName, room);
            }
            else
            {
                if (room.Members.Any(x => x.UserName == msg.ClientName))
                {
                    context.ReplyChannel.Reply(context, new JoinRoomResponse() { RetCode = JoinRoomRetCode.NameIsTaken });
                    return;
                }
                else
                {
                    room.Members.ForEach(m => m.UserChannel.Send(newEvent));
                    room.Members.Add(newMember);
                }
            }

            RoomMembers.Add(context.ReplyChannel, newMember);
            room.History.Add(newEvent);

            using (var tx = StateManager.CreateTransaction())
            {
                await _roomsState.SetAsync(tx, room.Name, room);
                await tx.CommitAsync();
            }

            context.ReplyChannel.Reply(context, new JoinRoomResponse() { RetCode = JoinRoomRetCode.Ok });

            foreach (var roomEvent in room.History)
            {
                context.ReplyChannel.Send(roomEvent);
            }
        }
        async Task OnRoomLeave(INetChannel channel)
        {
            RoomMember member;
            if (RoomMembers.TryGetValue(channel, out member))
            {
                var newEvent = new RoomEvent() { UserName = member.UserName, Text = "Left" };
                RoomData room;
                if (Rooms.TryGetValue(member.RoomName, out room))
                {
                    room.Members.Remove(member);
                    room.Members.ForEach(m => m.UserChannel.Send(newEvent));

                    room.History.Add(newEvent);

                    using (var tx = StateManager.CreateTransaction())
                    {
                        await _roomsState.SetAsync(tx, room.Name, room);
                        await tx.CommitAsync();
                    }
                }
            }
        }
        async Task OnSay(Say msg, IOperationContext context)
        {
            RoomMember member;
            if (RoomMembers.TryGetValue(context.ReplyChannel, out member))
            {
                RoomData room;
                if (Rooms.TryGetValue(member.RoomName, out room))
                {
                    var newEvent = new RoomEvent() {UserName = member.UserName, Text = msg.Text};
                    room.History.Add(newEvent);

                    using (var tx = StateManager.CreateTransaction())
                    {
                        await _roomsState.SetAsync(tx, room.Name, room);
                        await tx.CommitAsync();
                    }

                    room.Members.ForEach(m => m.UserChannel.Send(newEvent));
                }
            }
        }