2

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.

Loudenvier
  • 8,362
  • 6
  • 45
  • 66

2 Answers2

3

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!
Loudenvier
  • 8,362
  • 6
  • 45
  • 66
  • Nice extension, but you say `client.WaitForConnection(); // waits efficiently for client.IsConnected == true`. I doubt this is efficient, since it's really a busy wait. Quote from [busy waiting](http://en.wikipedia.org/wiki/Busy_waiting): `In general, however, spinning is considered an anti-pattern and should be avoided,[1] as processor time that could be used to execute a different task is instead wasted on useless activity.` – Noctis Jun 16 '14 at 06:36
  • 1
    @Noctis it's actually more efficient than you think. SpinWait is clever. It's a smart new addition to .NET. It will yield if it "thinks" its taking too long in a busy wait, it will even not busy wait at all if it's running in a single core processor. It is specifically build to be very efficient, not yielding when it is not actually needed. A short busy wait is MUCH more efficient than yielding the CPU by default. Take extreme care not to classify something as an anti-pattern based on its name alone. Docs: http://msdn.microsoft.com/en-us/library/vstudio/system.threading.spinwait – Loudenvier Jun 17 '14 at 05:19
  • 1
    weird, it didn't save the update ... Yes, After my first comment, I dig a bit deeper, specifically into the MSDN, and was actually satisfied that it's the right solution for the above. I then hit the edit to add that , but I guess it was lost. New thing learned :) – Noctis Jun 17 '14 at 05:36
  • For some reason, when trying to use your extension, it won't compile and keep complaining about ` client.Trigger(bindingName);` `bindingName` is a string, and it expects an `ITextArgs` or`IBinaryArgs`. What version are you using? – Noctis Jul 08 '14 at 04:21
  • @Noctis this is for version 3... I'm using specifically version 3.0.6.2 of the XSockets.Client library which I got from Nuget. – Loudenvier Jul 08 '14 at 05:19
  • Yep ...using the same version. Not sure how you fool your project to use a `string` as `ITextArgs` ... – Noctis Jul 08 '14 at 06:10
  • @Noctis this is very strange. Something is amiss on your side. This is the overload list for Trigger: `public void Trigger(IBinaryArgs payload);` `public void Trigger(ITextArgs payload);` `public void Trigger(string @ event);` `public void Trigger(ITextArgs payload, Action callback);` `public void Trigger(object obj, string @ event);` `public void Trigger(object obj, string @ event, Action callback);` As you can see Trigger can be called in a LOT of different ways, including as a string representing the event in question. – Loudenvier Jul 14 '14 at 03:14
2

I agree, the API's between JavaScript and C# should have been more similar. As of 4.0 they will both support both pub/sub and rpc and have the same naming on the interfaces.

The setting/getting of properties will have methods that help you since there is a difference in setting Enums and Strings for example. So the API will have methods like.. (note that you will multiplex over n controllers on 1 connections, that why the controller name is specified)

C#

conn.Controller("NameOfController").SetEnum("name", "value");
conn.Controller("NameOfController").SetProperty("name", object);

JavaScript

conn.nameofcontroller.setEnum('name', 'value');
conn.nameofcontroller.setProperty('name', object);

Regarding the error if you capitalize the "value" parameter in C#... Since we use the methods that the compiler created for public getters and setters value is really a parameter on a method and should not ever be capitalized.

Loudenvier
  • 8,362
  • 6
  • 45
  • 66
Uffe
  • 2,275
  • 1
  • 13
  • 9
  • I've updated my answer with a GetProperty method which makes it easy to get the properties. It seems I need to "trigger" a message to get the property value back... I don't know if this will "trigger" the message for all connected clients though... I've also created a strongly typed Bind method to bind to a strongly typed server action method, but I'm not sure it is 100% right. I didn't find a place in the XSockets website do contribute/discuss code, so I've posted it on my blog: http://feliperochamachado.com.br/blog/2014/05/strongly-typed-clients-in-xsockets-net/ (sometimes its slow...) – Loudenvier May 21 '14 at 01:22
  • Nice work! 4.0 will have a fix for the things you point out (like sync open, parameter order, method names, strongly typed binding etc). But it is awesome that version 3.* users can take advantage of your fixes. In the top menu on the site there is a link to our developer forum with ~750 developers so you can talk about code in there. I will check out your blog! – Uffe May 24 '14 at 05:46
  • I've written myself a few very useful extension methods I'll share there! Thanks for the amazing work. – Loudenvier May 24 '14 at 19:56