询问者
silverlight访问wcf异步方式变同步的问题

问题
-
我的思路是上面这个代码,但感觉不合适,代码臃肿,请问对这种异步调用等待线程结束用哪种方式更好?
public class A { public string ReturnString() { TestClient client = new TestClient(); string result = null; client.TestCompleted += delegate(object sender, GetCurrentTimeCompletedEventArgs e) { result = e.result; //想返回这个值 } //异步执行 client.TestAsync(); //等待 //返回 return result; } }
全部回复
-
Silverlight中为什么会不行?我类似下面的代码工作得很好啊~~
[ServiceContract] public interface ISomeService { [OperationContract(AsyncPattern = true)] IAsyncResult BeginDoSth(AsyncCallback ac, object state); string EndDoSth(IAsyncResult ar); }
用ChannelFactory创建client
BasicHttpBinding binding = new BasicHttpBinding(); EndpointAddress epa = new EndpointAddress(serviceUri); var client = new ChannelFactory<ISomeService>(binding, epa).CreateChannel();
现在就可以调用了
BackgroundWorker bk = new BackgroundWorker(); bk.DoWork += (sender, e)=>{client.BeginDoSth();client.EndDoSth();}; bk.RunWorkerAsync();
-
可能我们各自的意思没弄明白。
其实具体要求如下:
需要做一个函数:
function GetDBValue(){
var Rlt="";
//这个函数需要访问后台数据库才能确定Rlt的值。
return Rlt;
}
这个函数可在任何SL代码中调用。
1)如果能完成一个类似GetDBValue这样一个函数,就说明你可以把异步调用改为同步调用。
2)如果完成不了这个GetDBValue这个函数,就说明无法将异步模拟为同步。
请楼主给出一个实现GetDBValue函数的代码看看行不,十分感谢?我虽然研究SL有一年里,仍然无法用同步调用数据库的办法给出这样一个函数。
-
我想你遇到的主要问题不在于同步异步方面吧?我感觉最困难的地方在于Silverlight不支持DataTable类,从而需要定义海量的DataContract
在大约2009年,即Silverlight2时,遇到了类似的问题,为了返回服务端的各种数据,Silverlight需要定义一大堆的DataContract,一个简单的功能往往就需要定义20~30种DataContract,稍微复杂的就不可想象了,很多天的工作就是定义DataContract,纯粹的体力活:(
当时搞出的解决办法是:"动态类型"+自定义序列化.大致如下:
服务端:
[ServiceContract] public interface IDbService { [OperationContract] string Query(string strSql); } public class DbService : IDbService { public string Query(string strSql) { DataTable rlt = QueryInternal(strSql); string strCSV = SerializeToCSV(rlt); return strCSV; } private DataTable QueryInternal(strSql) { // 这里从略,标准的查询数据库,返回一个DataTable结果集 return datatableResult; } // 格式化为CSV形式,这是拷贝当时写的代码,对于当时的应用,CSV这种简单格式已经足够了,如果你的应用复杂,可以用标准的SOAP,JSON等序列化 private string SerializeToCSV(DataTable dtShow) { // 生成形如"Name:字段标题1,字段标题2\nType:System.String, System.Int32\n字串数据1, 100\n字串数据,512"样式的CSV StringBuilder sbCSV = new StringBuilder(); // Name & Type StringBuilder sbName = new StringBuilder("Name:"); StringBuilder sbType = new StringBuilder("Type:"); foreach (DataColumn c in dtShow.Columns) { if (c.Ordinal > 0) { sbName.Append(','); sbType.Append(','); } sbName.Append(c.ColumnName); sbType.Append(c.DataType.FullName); } sbCSV.AppendFormat("{0}\n{1}\n", sbName, sbType); // Data foreach (DataRow dr in dtShow.Rows) { foreach (DataColumn c in dtShow.Columns) { if (c.Ordinal > 0) sbCSV.Append(','); sbCSV.Append(dr[c]); } sbCSV.Append('\n'); } return sbCSV.ToString(); } }
客户端的关键在于反序列化,并生成动态数据类型
[ServiceContract] public interface IDbService { [OperationContract(AsyncPattern = true)] IAsnycResult BeginQuery(string strSql, AsyncCallback ac, object state); string EndQuery(IAsnycResult); } // 怎么创建客户代理见前面的贴子,这里从略 private void foo() { IAsnycResult ar = client.BeginQuery("some sql here", null, null); string rlt = client.EndQuery(ar); // rlt是CSV格式,或你自己的序列化后的DataTable // 下面要反序列化一下 TableCSV tcsv = new TableCSV(); List<object> listData = tcsv.Parse(strStat); // 可以使用反射访问数值,或者直接呈现给DataGrid yourDataGrid.ItemSource = listData; }
TableCSV是用来反序列化,并生成动态类型,如果你用了复杂的序列化,这里用相应的反序列化就可以了
<pre>// 把CSV格式的字符串型数据,转换为Table形式. public class TableCSV { public TableCSV() { } // 分析 public List<Object> Parse(string strCSV) { // 所有列结构描述应在所有数据之前 bool bColDescFin = false; // DataGridColumn属性正则表达式 Regex rgxProperties = new Regex(@"^(\w+):(.*)", RegexOptions.Singleline); // 换行分隔,逐行处理 IEnumerable<string> queryLines = from line in strCSV.Split('\n') select line; foreach (string strLine in queryLines) { Match m = rgxProperties.Match(strLine); if (m.Success) { // 分析列结构 ParseColumns(m.Groups[1].Value, m.Groups[2].Value); } else { if (!bColDescFin) { // 列结构分析结束,动态构造类型 bColDescFin = true; BuildType(); } // 分析数据 ParseData(strLine); } } return m_listData; } // 分析列结构 private void ParseColumns(string strE, string strColumns) { if (String.Compare(strE, "Name", StringComparison.OrdinalIgnoreCase) == 0) { // 分析列名称 m_listColumnName = new List<string>(); IEnumerable<string> queryColumns = from word in strColumns.Split(',') select word.Trim(); foreach (string strName in queryColumns) { m_listColumnName.Add(strName); } } else if (String.Compare(strE, "Type", StringComparison.OrdinalIgnoreCase) == 0) { // 分析列数据类型 m_listColumnType = new List<Type>(); IEnumerable<string> queryColumns = from word in strColumns.Split(',') select word.Trim(); foreach (string strTypeName in queryColumns) { try { m_listColumnType.Add(Type.GetType(strTypeName, true)); } catch (TypeLoadException ex) { string strbuf = String.Format("不可识别的数据类型\"{0}\"", strTypeName); throw new Exception(strbuf, ex); } } } else { string strbuf = String.Format("不可识别的前缀描述符\"{0}\",目前仅支持\"Name\"和\"Type\"两个描述符", strE); throw new Exception(strbuf); } } private List<string> m_listColumnName; private List<Type> m_listColumnType; // 动态生成的类型 private Type m_tStatItem = null; // 动态构造类型 private void BuildType() { try { AssemblyName asmName = new AssemblyName("AsmDynamic"); AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run); ModuleBuilder mBuilder = asmBuilder.DefineDynamicModule("ModDynamic"); TypeBuilder tBuilder = mBuilder.DefineType("StatItem", TypeAttributes.Public); for (int i = 0; i < m_listColumnName.Count; i++) { BuildProperty(tBuilder, m_listColumnName[i], m_listColumnType[i]); } m_tStatItem = tBuilder.CreateType(); } catch (ArgumentOutOfRangeException ex) { string strbuf = String.Format("列名称和类型数目不匹配,列名称{0}个,类型{1}个", m_listColumnName.Count, m_listColumnType.Count); throw new Exception(strbuf, ex); } } private void BuildProperty(TypeBuilder tBuilder, string strProperty, Type tProperty) { // 内部变量 string strField = String.Format("m_{0}", strProperty); FieldBuilder fldBuilder = tBuilder.DefineField(strField, tProperty, FieldAttributes.Private); // 定义属性 PropertyBuilder pBuilder = tBuilder.DefineProperty(strProperty, PropertyAttributes.None, tProperty, null); // get string strGet = String.Format("get_{0}", strProperty); MethodAttributes attrMethod = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; MethodBuilder mBuilder = tBuilder.DefineMethod(strGet, attrMethod, tProperty, Type.EmptyTypes); ILGenerator il = mBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, fldBuilder); il.Emit(OpCodes.Ret); pBuilder.SetGetMethod(mBuilder); // set string strSet = String.Format("set_{0}", strProperty); mBuilder = tBuilder.DefineMethod(strSet, attrMethod, null, new Type[] { tProperty }); il = mBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, fldBuilder); il.Emit(OpCodes.Ret); pBuilder.SetSetMethod(mBuilder); } // 分析数据 private void ParseData(string strData) { // 不处理空白行 if (strData.Trim() == String.Empty) return; int n = 0; string strValue = String.Empty; try { // 创建对象 object objItem = Activator.CreateInstance(m_tStatItem); IEnumerable<string> queryData = from word in strData.Split(',') select word.Trim(); foreach (string strbuf in queryData) { strValue = strbuf; // 数据类型转换 object objValue = Convert.ChangeType(strbuf, m_listColumnType[n], null); // 设定值 PropertyInfo pi = m_tStatItem.GetProperty(m_listColumnName[n]); pi.SetValue(objItem, objValue, null); n++; } m_listData.Add(objItem); } catch (ArgumentOutOfRangeException ex) { string strbuf = String.Format("定义了{0}列,该行数据不止{0}个:\n{1}", m_listColumnName.Count, strData); throw new Exception(strbuf, ex); } catch (FormatException ex) { string strbuf = String.Format("数据格式不相符.\n列\"{0}\"标识为\"{1}\"类型\n\"{2}\"第{3}列\"{4}\"无法转换为\"{1}\"类型", m_listColumnName[n], m_listColumnType[n], strData, n + 1, strValue); throw new Exception(strbuf, ex); } } // 结果数据 private List<object> m_listData = new List<object>(); }
-
补充几点说明:
- 当时我的应用中没有从数据库中返回大量结果,所以使用1个string作为交流中介足够了
- 如果你的应用中需要从数据库返回大量结果,则使用string显然是不够的,不过Silverlight支持Stream方式,这样可以解决大批量数据的问题
- 一般来说,Silverlight用于呈现最终结果给客户,应该是处理后的结果,海量的数据处理通常放在服务器端执行会更好一些,而处理后的结果通常是不大的
- Silverlight开始加强了对3D程序的支持,3D程序的一个特点就是不符合第3条,它通常需要渲染大量的顶点
- 我尚没有在Silverligth应用中渲染过大量的3D对象,但出于兴趣做过少量的简单测试,在没有硬件支撑的情况下,性能非常成问题;以后硬件支撑上来了,可能会变得非常有趣
-
T4Data系列100%满足你的要求。
T4DBConnection,T4DBCommand,T4DBParameter,T4DBParameterCollcetion,
T4DataTable,T4DataRow,T4DataRowCollection,T4DataColumn,T4DataColumnColletion
T4Data系列直接支持SL绑定,支持Linq,支持.net的基本数据类型(字符串,数值,布尔,字节数组,日期等),自动系列化,Server端0代码,0实体定义,SL与Server之间通讯全部封装无需开发者编写代码,传输做了简单加密 。
-
public class A { public string ReturnString() { TestClient client = new TestClient(); string result = null; client.TestCompleted += delegate(object sender, GetCurrentTimeCompletedEventArgs e) { result = e.result; //想返回这个值 } //异步执行 client.TestAsync(); //等待 //返回 return result; } }
你的代码含有错误,result可能在函数返回前没有被赋值。强行做同步处理的话,UI线程一旦被阻塞,浏览器就会卡,因此Silverlight里面的所有网络调用都是异步的。开发Silverlight程序就要适应异步服务调用模式。
Mog Liang
Please mark the replies as answers if they help or unmark if not.
If you have any feedback about my replies, please contact msdnmg@microsoft.com.
Microsoft One Code Framework- 已标记为答案 风云-魏永超Moderator 2011年3月2日 14:21
- 取消答案标记 学习.net 2011年4月13日 6:40
-
1)如果你不需要技术支持,可以不收费,就当是朋友。
2)如果你要技术支持,还是要收点费用。多少,由你说了算。
3)T4ToolTipService那个东西,我以后会修改为可以定制。当初只是想,本身就有箭头,里面只是文本而已,不会再用其他的东西进行装饰,没想到还需要自己定义里面的东西,所以当初就只是考虑了文字,是简单了点。如果大家要求强烈,我还是会改进的。
3)TFSoft的类库,及其简单易用。利用T4LibSla在SL端可以直接访问后台数据库,而后台不需要任何代码,也不需要任何实体定义。T4Lib的Data系列非常好用,已经有几家计算机软件设计企业采用,效果良好。(呵呵,虽然我是外行,但是那些计算机软件公司还是喜欢用外行做的东西)
-
1)如果你不需要技术支持,可以不收费,就当是朋友。
2)如果你要技术支持,还是要收点费用。多少,由你说了算。
3)T4ToolTipService那个东西,我以后会修改为可以定制。当初只是想,本身就有箭头,里面只是文本而已,不会再用其他的东西进行装饰,没想到还需要自己定义里面的东西,所以当初就只是考虑了文字,是简单了点。如果大家要求强烈,我还是会改进的。
3)TFSoft的类库,及其简单易用。利用T4LibSla在SL端可以直接访问后台数据库,而后台不需要任何代码,也不需要任何实体定义。T4Lib的Data系列非常好用,已经有几家计算机软件设计企业采用,效果良好。(呵呵,虽然我是外行,但是那些计算机软件公司还是喜欢用外行做的东西)
T4ToolTipService有文字就可以了,可惜它现在只能在当前鼠标所在的控件上显示提示,要是还能在指定的控件上显示提示就好了