Search code examples
c#xsockets.net

How to Get/Set Properties using the C# client API in XSockets


In the XSockets Server API there is an example on how to get/set properties on the server controller using the JavaScript API

Get/Set properties from the client APITop

If you have a property with a public getter or setter you can access the getter/setter methods from the client API’s

public string MyProp {get;set;}

The property above can be retrieved and changed from the client API’s (both JavaScript and C#). Example on how to set a new value from JavaScript

conn.publish('set_MyProp',{value:'NewValue'});

See the client API’s for more information.

But there's no information whatsoever on the Client API's page

I'm having a hard-time figuring out what is the equivalent C# Client code for the JavaScript code conn.publish('set_MyProp',{value:'NewValue'});

Any help is immensely appreciated.


Solution

  • 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:

    1. The methods have different names: publish in JavaScript vs Send in C#
    2. The parameters are passed in reversed order: value, event in C#, event, value in JavaScript
    3. 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!