Polymorphism in IQbservable query (Qactive)

Multi tool use
Polymorphism in IQbservable query (Qactive)
I am trying to implement a small project with the Qactive library from dave sexton.
It should be a full-duplex application that distinguishes between users and admins. An admin has all the functionalities of a user and additional functionalities.
I followed the chat example and created a class for the UserHooks and one for the AdminHooks which inherit accordingly.
https://github.com/RxDave/Qactive/blob/master/Examples/QbservableClient/ChatClient.cs
Now I tried to subscribe to the service that pushes as admin the admin hooks and as user the userhooks.
But how do I distungish in the IQbservable if I got an admin or user hook? I thought of something like casting with OfType, but then I only get the admin hooks but not the user hooks when I Subscribe. Is my model wrong or is there a way to solve this Problem?
EDITED
IQbservable<IServerToClientMessageBase> userQuery = client.Query(UserPrincipal.Current.SamAccountName)
.Do(hooks => connectOnMachineOutgoing.Subscribe(hooks.ConnectOnMachine))
.OfType<AdminHooks>()
.Do(hooks => createMachineOutgoing.Subscribe(hooks.CreateMachine))
.Do(hooks => startEditMachineOutgoing.Subscribe(hooks.StartEditMachine))
.Do(hooks => editMachineOutgoing.Subscribe(hooks.EditMachine))
.Do(hooks => cancelEditMachineOutgoing.Subscribe(hooks.CancelEditMachine))
.Do(hooks => deleteMachineOutgoing.Subscribe(hooks.DeleteMachine))
.Do(hooks => releaseMachineOutgoing.Subscribe(hooks.ReleaseMachine))
.SelectMany(hooks => hooks.MessagesForClient);
public class UserHooks
{
public UserHooks(
IObservable<IServerToClientMessageBase> messagesForClient,
IObserver<BasicMachineInformationMessage> connectOnMachine)
{
MessagesForClient = messagesForClient;
ConnectOnMachine = connectOnMachine;
}
// Client -> Server (Bound to Button click event on client)
public IObserver<BasicMachineInformationMessage> ConnectOnMachine { get; }
// Server -> Client
public IObservable<IServerToClientMessageBase> MessagesForClient { get; }
}
public class AdminHooks : UserHooks
{
public AdminHooks(
IObservable<IServerToClientMessageBase> messagesForClient,
IObserver<BasicMachineInformationMessage> connectOnMachine,
IObserver<CreateMachineMessage> createMachine,
IObserver<BasicMachineInformationMessage> startEditMachine,
IObserver<EditMachineMessage> editMachine,
IObserver<Unit> cancelEditMachine,
IObserver<BasicMachineInformationMessage> deleteMachine,
IObserver<BasicMachineInformationMessage> releaseMachine) : base(messagesForClient, connectOnMachine)
{
CreateMachine = createMachine;
StartEditMachine = startEditMachine;
EditMachine = editMachine;
CancelEditMachine = cancelEditMachine;
DeleteMachine = deleteMachine;
ReleaseMachine = releaseMachine;
}
// Client -> Server (Save Button - save new machine)
public IObserver<CreateMachineMessage> CreateMachine { get; }
// Client -> Server (Button - Open edit dialogue (lock machine for other clients))
public IObserver<BasicMachineInformationMessage> StartEditMachine { get; }
// Client -> Server (Save Button - save edit dialogue)
public IObserver<EditMachineMessage> EditMachine { get; }
// Client -> Server (Cancel Button - cancel edit dialogue (unlock machine for other clients))
public IObserver<Unit> CancelEditMachine { get; }
// Client -> Server (Delete Button - delete a machine)
public IObserver<BasicMachineInformationMessage> DeleteMachine { get; }
// Client -> Server (Release Button - kick connected user from machine)
public IObserver<BasicMachineInformationMessage> ReleaseMachine { get; }
}
As you see I have a client with multiple buttons for example to connect or edit machines. The machines are displayed in a grid. If someone connects to a machine all clients should get a notification over the MessagesForClient Observable in the UserHooks. The MessagesForClient pushes the IServerToClientMessageBase Interface, wich is implemented by a couple of different message types. Therefor it is possible to send different messages to the client for example when machine data changes or when the client should show a message box.
Furthermore I have to distungish between admin and normal users. Normal users can only connect to machines, in contrast to the admin users who have the same functionality than the normal users and can edit machines.
Because of that I created two hook classes for admin and user that inherit from each other.
The problem is now that I have to Subscribe to the admin hooks only if they where transferred, but also I need to get the IQbservable even if the client is a UserHook.
Possible Solution
I got a possible solution I haven't tested it until now, but I think it should work. I didn't know that IQbservable has a publish method. It allows me to subscribe multiple times to the IQbservable, but on the server. So it won't create multiple connections.
If you have a better solution, let me know it.
IQbservable<IServerToClientMessageBase> userQuery = client.Query(UserPrincipal.Current.SamAccountName)
.Publish(observable =>
observable
.Do(hooks => connectOnMachineOutgoing.Subscribe(hooks.ConnectOnMachine))
.OfType<AdminHooks>()
.Do(hooks => createMachineOutgoing.Subscribe(hooks.CreateMachine))
.Do(hooks => startEditMachineOutgoing.Subscribe(hooks.StartEditMachine))
.Do(hooks => editMachineOutgoing.Subscribe(hooks.EditMachine))
.Do(hooks => cancelEditMachineOutgoing.Subscribe(hooks.CancelEditMachine))
.Do(hooks => deleteMachineOutgoing.Subscribe(hooks.DeleteMachine))
.Do(hooks => releaseMachineOutgoing.Subscribe(hooks.ReleaseMachine))
.IgnoreElements()
.Merge(observable)
).SelectMany(hooks => hooks.MessagesForClient);
Your question is a bit unclear for me. Can you define your desired input and output of the query? Your example clearly shows that SamAccountName is the input, but there is no output; i.e., the entire query appears to be executed for its side effects only. Perhaps it's my fault that the chat example isn't clear about why I was using this "hook" pattern, but I'm not sure that it's the right approach for you. You may be able to get by with a simpler query. The hook is intended for intercepting subscriptions and disposals, that's all. If you don't need that, then it's more complex than you need.
– Dave Sexton
Jul 1 at 7:02
I should add that the "hook" class itself is not intended to be output data. As stated in my previous comment, it's merely meant to glue together the inputs and outputs while providing a way to intercept calls to Subscribe and Dispose.
– Dave Sexton
Jul 1 at 7:07
And one last thing, in the chat example the hook uses a Subject because the point of a chat application is to dispatch incoming messages to all other clients. A subject is perfect for this. So unless you're looking to dispatch messages to all connected clients, I would probably suggest not using a subject, and not using the hook pattern either.
– Dave Sexton
Jul 1 at 7:11
Sorry I forgot to subscribe to the observable for the server to client messages. I edited my question and hope it is clear now. Maybe you have a better suggestion to solve this Problem.
– Okarin
Jul 3 at 9:13
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
what is a hook?
– cwharris
Jun 18 at 6:21