Silverlight : appel de services RPC (WCF ou Soap) en synchrone

Je me suis bien marré ce matin, ça fait du bien.


J’étais en train de chercher un moyen de faire des appels en synchrone en Silverlight. Jusqu’à présent le pattern que nous connaissons tous est de s’enregistrer sur un event Completed puis de lancer l’appel, celui-ci se fait en asynchrone puis le retour de la méthode génère l’événement completed.


Malheureusement je suis face à un cas, où je dois être en synchrone obligatoirement ; je travaille sur un player elarning en Scorm basé sur Silverlight. Le scorm utilise des méthodes (initialize, setvalue, getvalue, etc.) qui permettent de faire une communication avec un cours via Javascript.


Mes méthodes sont déportées aussi sur un frontage de service WCF. Mon premier essai fut un desastre : lorsque mon cours (en Flash caca) appelle l’une de ces méthodes, il passe d’abord par le Javascript qui remonte ensuite vers le Silverlight qui appelle la méthode sur le service WCF.


Oui mais la méthode renvoie tout de suite : elle est en asynchrone ! Donc la fonction JS aussi, donc le cours plante lamentablement…


J’ai donc cherché un moyen de faire des appels Synchrones. Premiere étape de tout bon développeur : Bing ! Je tombe alors sur un article d’un mec nommé Daniel Vaughan vraiments très très bon sur codeproject. En fait un peu comme les miens :p … sauf que lui quand il parle anglais ca inspire le respect, … fin bref.


Article interessant mais qui est à mes yeux faux : Il prétend avoir trouvé un moyen de faire du synchrone en Silverlight, je vous laisse juger par vous même :


http://www.codeproject.com/KB/silverlight/SynchronousSilverlight.aspx


Bien écrit, propre, clair, mais son asynchrone est en fait un appel synchronizé maladroitement sur un thread asynchrone … bof. L’étiquette ne correspond pas au produit.


Sur bing on ne trouve rien d’autre, sur google aussi d’ailleur… J’ai donc cherché un autre moyen.


Comme je suis un fan des Rias, je suis revenu aux sources : XmlHttpRequest et là j’ai eu l’idée !


Je fais un appel de mon service WCF via Javascript à l’aide d’une requête XmlHttpRequest. La fonction Javascript qui contient cet appel est générique et peut cibler des endpoints de services différents et des méthodes différentes. Le tout quelque soit le nombre de paramètres (en gros : no limit). La fonction javascript se présente comme ceci :


 


function SyncWCFServiceCaller()
{
var client = null;
if (window.XMLHttpRequest)
client = new XMLHttpRequest();
else if (window.ActiveXObject)
client = new ActiveXObject(“Microsoft.XMLHTTP”);
var data = arguments[1];
client.open(‘POST’, arguments[0], false);
client.setRequestHeader(‘Content-Type’, ‘application/json; charset=utf-8′);
client.setRequestHeader(‘Content-Length’, data.length + ”);
client.send(data);
var response = eval(‘(‘ + client.responseText + ‘)’).d;
return response;
}



 


Je l’ai placée dans la testpage html auto générée de Silverlight. J’utilise le mot clé arguments pour acceder à l’array des paramètres passés à la méthode. Je créé un objet XmlHttp, j’indique l’url du service suivit du nom de la méthode (http://localhost/service.svc/dowork). Dans le second paramètre se trouve une serialisation Json des paramètres à passer à la méthode (du type {“name” : “valentin”}).


Coté C# j’ai créé un petit manager qui me permet d’appeller cette méthode en lui donnant l’url du service et les paramètres serialisés :


 


public class ContractToJavascriptCaller<T>
{
public string ServiceEndPointUri
{
get;
private set;
}



public ContractToJavascriptCaller(string serviceEndPointUri)
{
   this.ServiceEndPointUri = serviceEndPointUri;
}

public object CallMethod(string methodName, params string[] arguments)
{
  StringBuilder builder = new StringBuilder();
  builder.Append(“{“);

  MethodInfo methodInfo = typeof(T).GetMethod(methodName);
  ParameterInfo[] parameters = methodInfo.GetParameters();

  int parameterIndex = 0;
  foreach (ParameterInfo parameter in parameters)
  {
    builder.Append(“\””);
    builder.Append(parameter.Name);
    builder.Append(“\””);
    builder.Append(“:\””);
    builder.Append(arguments[parameterIndex++]);
    builder.Append(“\”,”);
  }
 string jsonSerializedParameters = builder.ToString().TrimEnd(‘,’);
 jsonSerializedParameters = string.Concat(jsonSerializedParameters, “}”);
 
  return HtmlPage.Window.Invoke(“SyncWCFServiceCaller”, string.Concat(this.ServiceEndPointUri, “/”, methodName), jsonSerializedParameters);
  }
}


 


C’est la méthode CallMethod qui fait tout le travail. Mon serialiser JSon à l’intérieur est bidon et constitue l’axe d’amélioération de mon code : il ne travail que sur les paramètres de type string : bof. J’appelle la fonction Javascript en lui donnant le nom de la méthode et l’uri du service concatènés et les paramètres sérialisés en JSon. La serialisation Json se base sur l’étude du type spécifique de la classe ContractToJavascriptCaller (nommé T). Il etudie la méthode à appeller et en tire les paramètres à l’aide de la reflexion.


L’appel à une méthode en Synchrone est alors exessivement simple :


ContractToJavascriptCaller<IService> caller = new ContractToJavascriptCaller<IService>(http://localhost:23770/Service.svc);
this.ResultTextBlock.Text = = caller.CallMethod(“DoWork”, this.firstMathodTextBlock.Text).ToString();


Pourquoi s’ennuyer avec des techniques fastidieuses quand tout est simple ? :)


[soon]


Valentin

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>