Well, I've found out the hard way, by trial an error, that this:
Client.Send(new { value = "NewValue" }, "set_MyProp");
Is the equivalent code to:
conn.publish('set_MyProp',{value:'NewValue'});
Pay attention to the case of "value"!!! Don't capitalize the word!
UPDATE
I've created two extension methods which makes it very easy to get and set property values (there's also a WaitForConnection
which is useful in somewhat synchronous scenarios like Unit Testing).
Since XSockets is (very unfortunately) not open source, and documentation is minimal, I have to guess how things work, so my extension methods may not be as efficient and elegant as they could be if I was able to read the source code, and I may have been a little too "careful" with my approach for reading properties from the server... If anyone knows how to improve it, please, edit the answer or suggest it on the comments section:
public static class XSocketClientExtensions
{
public static bool WaitForConnection(this XSocketClient client, int timeout=-1) {
return SpinWait.SpinUntil(() => client.IsConnected, timeout);
}
public static void SetServerProperty(this XSocketClient client, string propertyName, object value) {
client.Send(new { value = value }, "set_" + propertyName);
}
public static string GetServerProperty(this XSocketClient client, string propertyName) {
var bindingName = "get_" + propertyName;
// why event name is lowercase?
var eventName = bindingName.ToLowerInvariant();
// we must be careful to preserve any existing binding on the server property
var currentBinding = client.GetBindings().FirstOrDefault(b => b.Event == eventName);
try {
// only one binding at a time per event in the client
if (currentBinding != null)
client.UnBind(bindingName);
var waitEvent = new ManualResetEventSlim();
string value = null;
try {
client.Bind(bindingName, (e) => {
value = e.data;
waitEvent.Set();
});
// we must "Trigger" the reading of the property thru its "event" (get_XXX)
client.Trigger(bindingName);
// and wait for it to arrive in the callback
if (waitEvent.Wait(5000))
return value;
throw new Exception("Timeout getting property from XSockets controller at " + client.Url);
} finally {
client.UnBind(bindingName);
}
} finally {
// if there was a binding already on the "property getter", we must add it back
if (currentBinding != null)
client.Bind(bindingName, currentBinding.Callback);
}
}
}
Usage is a breeze:
// Custom controller
public class MyController : XSocketController
{
public int Age { get; set; }
public override void OnMessage(ITextArgs textArgs) {
this.SendToAll(textArgs);
}
}
// then in the client
var client = new XSocketClientEx("ws://127.0.0.1:4502/MyController", "*");
client.WaitForConnection(); // waits efficiently for client.IsConnected == true
client.SetServerProperty("Age", 15);
int age = Convert.ToInt32(client.GetServerProperty("Age"));
You can skip the following, it is just a rant!
A few things made it difficult to nail it down from the beginning. There's no agreement between the JavaScript client and the C# client. So what you learn in one does not translate to the other technology. On my own multi-language client APIs I try to make all APIs behave and look very similar, if not identical, so code is almost portable. I've got an API that looks nearly identical in both JavaScript, C# and Java.
The differences that bothered me are:
- The methods have different names:
publish
in JavaScript vs Send
in C#
- The parameters are passed in reversed order:
value, event
in C#, event, value
in JavaScript
Not a difference in itself, but if you use 'Value' in the C# example it won't work, you must use 'value'... I don't believe it should have been case sensitive... the reasoning for this is quite convincing. I'll quit quibbling then!