在软件开发中,同步调用是一种常见的需求,尤其是在客户端与服务器之间的通信中。本文将介绍一种在Silverlight应用程序中实现同步调用WCF服务的方法。这种方法通过JavaScript和Silverlight的结合使用,解决了同步调用中常见的问题,如UI线程冻结等。
在阅读了CodeProject上Daniel Vaughan关于Silverlight同步调用的文章后,对他的实现方式持有不同意见。他的方法在异步线程中实现了同步调用,这在大多数同步问题中并不适用。在应用程序中,需要在Silverlight应用和LMS服务器(提供WCF服务)之间创建一个通信管道,因此需要一种更有效的同步调用方法。
解决方案相对简单,使用了JavaScript,这是Silverlight的“血亲”,来帮助通过XmlHttpRequest对象调用WCF服务。创建了一个通用的JS函数,它将调用WCF前端的指定方法并添加指定的参数。
这个函数通过arguments数组读取由管理器传递的参数:
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;
}
arguments[0]包含WCF端点和方法名称(例如 "localhost/service.svc/method")。arguments[1]包含序列化的参数(以JSON格式),如 {"name" : "daniel"}。这里没有复杂的内容。XmlHttpRequest创建了一个同步调用,所以如果它从Silverlight调用,它不会在服务返回之前返回调用者。
C#管理器也非常简单:
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("\"").Append(parameter.Name).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);
}
}
这个管理器只有一个方法CallMethod,它将使用服务端点和方法名称以及序列化为JSON的参数来调用JS函数。HtmlPage.Window.Invoke不会在WCF服务调用之前返回。
WCF服务包含两个方法:
string DoWork(string name);
string DoWorkWithTwoParams(string name, string name2);
在它们的指令块中放入了一个Thread.Sleep指令,以模拟一组大的计算指令。WCF服务的接口在Silverlight项目中链接。所以调用WCF方法,只需要做:
ContractToJavascriptCaller<IDanielService> caller = new ContractToJavascriptCaller<IDanielService>("http://localhost:23770/DanielService.svc");
string response = caller.CallMethod("DoWork", this.firstMathodTextBlock.Text).ToString();
this.ResultTextBlock.Text = response;
对于UI线程冻结的问题,这种方法可以避免,因为它不会冻结浏览器。