我想交换一对嵌套IObservable的排序,并对它发生的方式有几个约束.
作为一个具体的(虽然有点做作)的例子,假设有一个游戏服务器托管连续的在线多人游戏.玩家可以随时加入并在每个游戏中执行操作.以下类具有工作实现,其提供玩家在连续游戏期间执行的动作的只读视图:
class GameServer
{
public IObservable<Game> Games { get { ... } }
}
class Game
{
public int GameId { get { ... } }
public IObservable<GamePlayer> Players { get { ... } }
}
class GamePlayer
{
public int PlayerId { get { ... } }
public IObservable<PlayerAction> PlayerActions { get { ... } }
}
在这些类中,有一个嵌套的可观察IObservable< IObservable< IObservable< PlayerAction>>>.这是给出形式的信息:有一系列游戏.在每场比赛中,一系列球员加入.每个玩家在游戏中执行了许多动作.
我想要做的是重新排列这些数据,以便它提供以下信息:有许多玩家.由于每个玩家加入,已经玩了一系列游戏.在每个游戏中,玩家执行了许多动作.这看起来像是以下方法的实现:
IObservable<Player> Players { get; }
使用以下类:
class Player
{
public Player(int playerId, IObservable<PlayerGame> games)
{
PlayerId = playerId;
Games = games;
}
public int PlayerId { get; private set; }
public IObservable<PlayerGame> Games { get; private set; }
}
class PlayerGame
{
public PlayerGame(int gameId, IObservable<PlayerAction> gameActions)
{
GameId = gameId;
GameActions = gameActions;
}
public int GameId { get; private set; }
public IObservable<PlayerAction> GameActions { get; private set; }
}
不是提供一系列游戏并为每个玩家展示每个玩家所做的事情,而是提供一系列玩家并展示他们参与连续游戏.
还有一个额外要求:一旦玩家加入,他们对每个连续游戏的动作都应该显示出来,无论他们是否在游戏过程中做了什么(即如果玩家在游戏过程中什么也不做,玩家应该仍然推出新玩家游戏时游戏开始,即使它的GameActions从不推动价值).
我如何使用GameServer.Games作为相关数据的来源实现玩家?
(回应DaveSexton的评论:ID代表什么,数据来自何处以及程序运行的框架或环境并不重要.所有必需的数据都存在于GameServer,Game和GamePlayer类中.我只是不喜欢我不知道怎么把它改组成播放器形式.)
一个几乎可行的解决方案
为了更好地了解我的目标,这里有一个几乎可行的解决方案.唯一的问题是,如果一个新游戏开始并且GameServer.Games推送一个新游戏,则Player.Games不会相应地为每个现有玩家推送一个新的PlayerGame(我喜欢它).
Players = gameServer.Games
.Select(g => g.Players.Select(p => new { g.GameId, p.PlayerId, p.PlayerActions }))
.Switch()
.GroupBy(t => t.PlayerId)
.Select(
group =>
new Player(group.Key, group.Select(t => new PlayerGame(t.GameId, t.PlayerActions))));
解决方法: 根据OP提供的新信息和示例查询更新了答案.
Players = Games.Publish(publishedGames =>
from game in publishedGames
from player in game.Players
select new Player(
player.PlayerId,
(from game2 in publishedGames
from player2 in game2.Players
where player2.PlayerId == player.PlayerId
select new PlayerGame(game2.GameId, player2.PlayerActions))
.StartWith(new PlayerGame(game.GameId, player.PlayerActions))))
.Distinct(player => player.PlayerId)
这实际上只是一个SelectMany查询.对于每个游戏,对于每个GamePlayer,它都会投射一个新玩家.
最初的问题是如何从GamePlayer创建一个新的播放器.您在示例查询中显示的是您只想从Game对象转换PlayerGame对象;因此,我刚刚使用内部查询来过滤具有玩家ID的游戏.而已.
发布仅用于游戏很冷的情况.如果它很热,那么你不需要发布.
我添加了Distinct,因为没有它,只要观察到已经观察过的玩家的游戏,就会再次观察到这些玩家. 来源:https://www./content-1-278101.html
|