none
silverlight访问wcf异步方式变同步的问题 RRS feed

  • 问题

  • 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;
    
      }
    
    }
    
    我的思路是上面这个代码,但感觉不合适,代码臃肿,请问对这种异步调用等待线程结束用哪种方式更好?
    2011年2月14日 3:08

全部回复

  • 1)你的代码不仅是代码臃肿,而且是不可能实现的。

    2)即使你的代码可以实现,也会造成UI界面阻塞。

    3)实践证明,不要试图改异步为同步,不管你费多大力气,最终都是徒劳。

    4)要用同步模式,你只有使用XmlHttp来代替wcf,或使用本人研制的T4LibSla类库。TFSoft已经研制了一个类似ado.net 的支持同步/异步方式的在SL端直接访问后台数据库的,大数据量时速度比WCF稍快点类库。

    2011年2月14日 3:37
  • 1)你的代码不仅是代码臃肿,而且是不可能实现的。

    2)即使你的代码可以实现,也会造成UI界面阻塞。

    3)实践证明,不要试图改异步为同步,不管你费多大力气,最终都是徒劳。

    4)要用同步模式,你只有使用XmlHttp来代替wcf,或使用本人研制的T4LibSla类库。TFSoft已经研制了一个类似ado.net 的支持同步/异步方式的在SL端直接访问后台数据库的,大数据量时速度比WCF稍快点类库。

    广告吗?
    2011年2月14日 8:52
  • 等你走了弯路以后,你会发现那不是广告而恰恰是忠告。
    2011年2月14日 9:03
  • client.BeginDoSth(...);
    // End方法会阻塞
    var retsult = client.EndDoSth(...);
    
    

    做你喜欢的吧,我有时也喜欢这么搞法;比如,我单独开一个BackgroundWorker,然后在它的DoWork里用同步代码,这样不会阻塞界面,代码也简单

    2011年2月14日 10:06
  • 1)提问人的主要目的是想做一个函数。这个函数必须访问后台数据库,并且任何地方都可以直接调用。

    2)只要采用异步方式访问数据库,他这个函数是无法实现的,除非他不采用异步方式访问数据库,或者重新书写SL内核。

    TFSoft的再次“广告”!

    2011年2月14日 12:17
  • AceBear

    在.net中我们确实可以这样:

    client.BeginDoSth(...);
    // End方法会阻塞
    var retsult = client.EndDoSth(...);
    

    但是在Silverlight中:

    应该没有办法。

    不知道你是否在SL中测试过?

    2011年2月15日 9:52
  • 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();
    2011年2月16日 5:29
  • 可能我们各自的意思没弄明白。

    其实具体要求如下:

    需要做一个函数:

    function GetDBValue(){

    var Rlt="";

    //这个函数需要访问后台数据库才能确定Rlt的值。

    return Rlt;

    }

    这个函数可在任何SL代码中调用。

     

    1)如果能完成一个类似GetDBValue这样一个函数,就说明你可以把异步调用改为同步调用。

    2)如果完成不了这个GetDBValue这个函数,就说明无法将异步模拟为同步。

    请楼主给出一个实现GetDBValue函数的代码看看行不,十分感谢?我虽然研究SL有一年里,仍然无法用同步调用数据库的办法给出这样一个函数。

     

    2011年2月16日 5:57
  • string GetDBValue()
    {
      var ar = client.BeginXXX(null, null);
      var Rlt = client.EndXXX(ar);
      return Rlt;
    }
    
    是这个意思吗?

    2011年2月16日 6:07
  • 是。能否给出一个完整的例子?

    就是需要构造一个SL端可直接调用的返回服务器端数据库内容的函数!

    谢谢。

    2011年2月16日 8:12
  • 我想你遇到的主要问题不在于同步异步方面吧?我感觉最困难的地方在于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>();
    }
    

     

    2011年2月16日 9:54
  • 补充几点说明:

     

    1. 当时我的应用中没有从数据库中返回大量结果,所以使用1个string作为交流中介足够了
    2. 如果你的应用中需要从数据库返回大量结果,则使用string显然是不够的,不过Silverlight支持Stream方式,这样可以解决大批量数据的问题
    3. 一般来说,Silverlight用于呈现最终结果给客户,应该是处理后的结果,海量的数据处理通常放在服务器端执行会更好一些,而处理后的结果通常是不大的
    4. Silverlight开始加强了对3D程序的支持,3D程序的一个特点就是不符合第3条,它通常需要渲染大量的顶点
    5. 我尚没有在Silverligth应用中渲染过大量的3D对象,但出于兴趣做过少量的简单测试,在没有硬件支撑的情况下,性能非常成问题;以后硬件支撑上来了,可能会变得非常有趣

     

    2011年2月16日 10:13
  • T4Data系列100%满足你的要求。

    T4DBConnection,T4DBCommand,T4DBParameter,T4DBParameterCollcetion,

    T4DataTable,T4DataRow,T4DataRowCollection,T4DataColumn,T4DataColumnColletion

    T4Data系列直接支持SL绑定,支持Linq,支持.net的基本数据类型(字符串,数值,布尔,字节数组,日期等),自动系列化,Server端0代码,0实体定义,SL与Server之间通讯全部封装无需开发者编写代码,传输做了简单加密 。

     

    2011年2月16日 11:25
  • 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
    2011年2月21日 10:26
  • 我也试图把异步变。为此,我进行了无限的实验,但是没有成功。

    可是楼上有人硬说可以,我就比较郁闷。但是那些说可以的人,他们只是给出代码的大致结构,却硬是不肯给出可以实际运行的代码。所以我猜想那些说可以的人,可能是按照.net全框架编程惯性思维作出的想象而已。

     

     

    2011年2月21日 12:04
  • 我同意TFsoft的观点。如果在函数的中间要访问WCF后得出数据,那结果就不无法在本函数中取得。
    2011年3月5日 3:49
  • 要走捷径,用TFsoft的是个好办法。

    因为自己已经也做了个类似的东西,所以没试过TFsoft的,肯定比我自己的好,但不知收费否?也想试试。

    顺便谢谢TFsoft的Tip提示类,很好用 :)  希望再加个“在制定控件上提示的功能"

    2011年3月7日 1:06
  • 1)如果你不需要技术支持,可以不收费,就当是朋友。

    2)如果你要技术支持,还是要收点费用。多少,由你说了算。

    3)T4ToolTipService那个东西,我以后会修改为可以定制。当初只是想,本身就有箭头,里面只是文本而已,不会再用其他的东西进行装饰,没想到还需要自己定义里面的东西,所以当初就只是考虑了文字,是简单了点。如果大家要求强烈,我还是会改进的。

    3)TFSoft的类库,及其简单易用。利用T4LibSla在SL端可以直接访问后台数据库,而后台不需要任何代码,也不需要任何实体定义。T4Lib的Data系列非常好用,已经有几家计算机软件设计企业采用,效果良好。(呵呵,虽然我是外行,但是那些计算机软件公司还是喜欢用外行做的东西)

     

    2011年3月7日 3:46
  • 1)如果你不需要技术支持,可以不收费,就当是朋友。

    2)如果你要技术支持,还是要收点费用。多少,由你说了算。

    3)T4ToolTipService那个东西,我以后会修改为可以定制。当初只是想,本身就有箭头,里面只是文本而已,不会再用其他的东西进行装饰,没想到还需要自己定义里面的东西,所以当初就只是考虑了文字,是简单了点。如果大家要求强烈,我还是会改进的。

    3)TFSoft的类库,及其简单易用。利用T4LibSla在SL端可以直接访问后台数据库,而后台不需要任何代码,也不需要任何实体定义。T4Lib的Data系列非常好用,已经有几家计算机软件设计企业采用,效果良好。(呵呵,虽然我是外行,但是那些计算机软件公司还是喜欢用外行做的东西)

     

    T4ToolTipService有文字就可以了,可惜它现在只能在当前鼠标所在的控件上显示提示,要是还能在指定的控件上显示提示就好了

     

    2011年3月8日 7:21
  • 我也试图把异步变。为此,我进行了无限的实验,但是没有成功。

    可是楼上有人硬说可以,我就比较郁闷。但是那些说可以的人,他们只是给出代码的大致结构,却硬是不肯给出可以实际运行的代码。所以我猜想那些说可以的人,可能是按照.net全框架编程惯性思维作出的想象而已。

     

     

    大哥,看了这么多回复,我发现真的只有你理解这个意图,并之前实践过。可以私聊一下吧。
    2011年4月13日 6:42
  • 是的,经过多种方式偿试都没有成功过,UI线程绝对卡死。没有实践就没有真理。我现在都放弃使用同步方式了,但使用异步,的确是非常的麻烦。
    2011年4月13日 6:46
  • QQ:342401768

    不经常在线,请留言联系。

    2011年4月27日 3:27
  • AceBear的方法不错,但看不明白

    public interface IDbService
    {
      [OperationContract(AsyncPattern = true)]
      IAsnycResult BeginQuery(string strSql, AsyncCallback ac, object state);
      string EndQuery(IAsnycResult);
    }

    这段是在服务端定义,还是在客户端定义,AceBear能不能给一个完整的demo,让大家学习学习,谢谢

    2011年5月13日 8:32
  • 他的方法是臆想的,你当然看不明白。

    2011年5月15日 18:39
  • 应该是无法实现同步的。理论上行不通。
    2015年6月25日 3:02