none
SilverLight 2(Beta 2)是必须使用异步调用的方式调用WCF服务吗? RRS feed

  • 问题

  • 在SilverLight工程下添加WCF服务引用,生成出来的客户端代码都是异步调用的方式实现的(可参见微软的SilverLight HandOnLab文档《SL2B2-HOL-LayoutAndData》 )。另外考虑SilverLight下的WebClient和HttpWebRequest等类,在SilverLight工程下都只暴露了异步调用的接口。请问:

    1. 为什么SilverLight下一定要使用这种异步调用的方式呢,常用的同步调用方式会造成什么问题吗?

    2. 如果我在普通工程下添加WCF服务引用,生成的可同步调用的客户端Proxy代码,稍作修改后可以在SilverLight工程中调用吗?

    初到宝地,希望有能人帮忙解答,谢谢:)
    2008年7月28日 7:06

答案

  • 微软美国产品组关于SilverLight下为何只能支持异步方式的答复如下:

     

    The primary reason is that the browser plugin networking APIs only support
    async calls on the UI thread.    This means we are blocked from supporting
    sync requests on the UI thread.  (If we blocked on the UI thread, we would never get the response callback from the browser.)  Looking at an API friendliness, scenarios, and resourcing priority, we don’t support a “simulated” sync call on a background thread.

     

    即UI线程如果被阻塞的话,是不能得到异步响应结果的。所以楼上所建议的方法可能在SliverLight下行不通的。关于这个问题,有一个简单的通过WebRequest异步调用的示例如下,如果有同志实现了将该异步调用模拟为同步调用的话请及时回复此帖:

     

            private void Button_Click(object sender, RoutedEventArgs e)
            {
                Uri endpoint = new Uri(urlstr);
                WebRequest request = WebRequest.Create(endpoint);
                request.BeginGetResponse(new AsyncCallback(ResponseReady), request);

               

                //“simulated” sync call : do something here to get response


            }

            void ResponseReady(IAsyncResult asyncResult)
            {
                WebRequest request = asyncResult.AsyncState as WebRequest;
                WebResponse response = request.EndGetResponse(asyncResult);


                using (Stream responseStream = response.GetResponseStream())
                {
                    StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8);

                    string response = readStream.ReadToEnd();
                }

            }

     

    另外:一定要同步得到响应的话,可以通过SilverLight调用Javascript实现,在Javascript中使用XMLHttpRequest类即可。

    2008年8月21日 6:28

全部回复

  •  日日野 写:

    在SilverLight工程下添加WCF服务引用,生成出来的客户端代码都是异步调用的方式实现的(可参见微软的SilverLight HandOnLab )。另外考虑SilverLight下的WebClient和HttpWebRequest等类,在SilverLight工程下都只暴露了异步调用的接口。请问

    1. 为什么SilverLight下一定要使用这种异步调用的方式呢,常用的同步调用方式会造成什么问题吗?

    2. 如果我在普通工程下添加WCF服务引用,生成的可同步调用的客户端Proxy代码,稍作修改后可以在SilverLight工程中调用吗?

    初到宝地,希望有能人帮忙解答,谢谢:)

     

    所谓异步调用,就是在你主线程进行其他操作的时候,还在进行下载请求,不用等待函数的返回值。如果想要进行同步操作,可以用WebClient的DownloadProgressChanged判断,当未下载完成的时候,暂停主线程其他操作,等下载完成之后再开始。异步调用可以说是网站程序的一大优点,Ajax一大优势也就是可以异步调用,SilverLight更是可以异步调用的RIA。

     

    SilverLight目前只支持HttpBasic方式的WCF调用。其他协议目前还不支持

    2008年7月28日 7:34
    版主
  •  

    感谢您的回复:)

     

    对于第一个问题,焦点在于SilverLight下是否“只能异步调用”。SilverLight下“可以异步调用”我没有任何意见,现在看到的所有的例子都是使用的异步调用的方式,我现在非常想看到一个在SliverLight下能同步调用WCF服务的例子。您通过DownloadProgressChanged事件实现同步调用的方法是在异步调用情况下一个变通的实现方式,和一般理解的同步调用还是有区别的吧。

     

    对于第二个问题,我刚才简单试了试,想把普通工程下生成的可同步调用的Proxy代码文件转成SliverLight下可用的代码文件好像是不行的。

     

     

    2008年7月28日 8:42
  •  日日野 写:

     

    感谢您的回复:)

     

    对于第一个问题,焦点在于SilverLight下是否“只能异步调用”。SilverLight下“可以异步调用”我没有任何意见,现在看到的所有的例子都是使用的异步调用的方式,我现在非常想看到一个在SliverLight下能同步调用WCF服务的例子。您通过DownloadProgressChanged事件实现同步调用的方法是在异步调用情况下一个变通的实现方式,和一般理解的同步调用还是有区别的吧。

     

    对于第二个问题,我刚才简单试了试,想把普通工程下生成的可同步调用的Proxy代码文件转成SliverLight下可用的代码文件好像是不行的。

     

     

     

    异步调用:一个可以无需等待函数的返回值就让操作继续进行的函数。

    同步调用:需要等待返回值在进行操作。

     

    举个简单的例子小明去沏茶,要烧水,接水,拿茶叶,洗水壶,洗茶杯,沏茶.

    在接水,烧水的时候去干拿茶叶,洗水壶,洗茶杯这就是一个异步操作

    等水烧开再做拿茶叶,洗水壶,洗茶杯就是一个同步操作

     

     

    你把触发事件写在Completed事件之中,就是同步调用方式

    你把时间分别写在ButtonClick跟Completed下就是一种异步调用方式

     

    楼主不就想调用一个方法没有返回值程序就在那里死等么?你理解的同步调用方式是如何的?我觉得同步跟异步上只是程序上的写法不同。就好比Ajax的异步方式,不刷新页面调用HttpRequest来进行操作,就是异步了。

    WinForm建立线程进行数据操作,程序也就变成异步的了。而SilverLight要想变成一个同步的方式,只要你把你的事件写在Completed事件中,这就是一个同步中的程序了,不要忘了网络的延迟性,偶们必须等等的时候你去做别的了,就是一个异步操作,等的时候你什么也不干,也不让别人干,那就是同步咯
    2008年7月28日 9:58
    版主
  • 微软美国产品组关于SilverLight下为何只能支持异步方式的答复如下:

     

    The primary reason is that the browser plugin networking APIs only support
    async calls on the UI thread.    This means we are blocked from supporting
    sync requests on the UI thread.  (If we blocked on the UI thread, we would never get the response callback from the browser.)  Looking at an API friendliness, scenarios, and resourcing priority, we don’t support a “simulated” sync call on a background thread.

     

    即UI线程如果被阻塞的话,是不能得到异步响应结果的。所以楼上所建议的方法可能在SliverLight下行不通的。关于这个问题,有一个简单的通过WebRequest异步调用的示例如下,如果有同志实现了将该异步调用模拟为同步调用的话请及时回复此帖:

     

            private void Button_Click(object sender, RoutedEventArgs e)
            {
                Uri endpoint = new Uri(urlstr);
                WebRequest request = WebRequest.Create(endpoint);
                request.BeginGetResponse(new AsyncCallback(ResponseReady), request);

               

                //“simulated” sync call : do something here to get response


            }

            void ResponseReady(IAsyncResult asyncResult)
            {
                WebRequest request = asyncResult.AsyncState as WebRequest;
                WebResponse response = request.EndGetResponse(asyncResult);


                using (Stream responseStream = response.GetResponseStream())
                {
                    StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8);

                    string response = readStream.ReadToEnd();
                }

            }

     

    另外:一定要同步得到响应的话,可以通过SilverLight调用Javascript实现,在Javascript中使用XMLHttpRequest类即可。

    2008年8月21日 6:28
  • 我说的方法应该算是模拟同步模式一种解决办法吧,把主线程一直单步执行下去,即使调用JavaScript,XMLHttpRequest本来就是一个异步的操作。没什么差啦。你依旧不能锁场景线程

    2008年8月21日 7:04
    版主
  •  

    另XMLHttpRequest方法无非也就相当于把BeginGetResponse方法放在SilverLight之外了而已。是判断ReadComplet更容易点,还是拿出去在HTMLDOM再HttpRequest更方便,对功能上的影响也都没差吧。
    2008年8月21日 7:09
    版主
  • 对于异步的方法的问题。
    微软内部的人员也有分歧,
    但是最后确定使用异步,最重要的原因就是在浏览器上不可能为了等一个操作而花费时间。

    2008年8月23日 17:20
  •  日日野, 你好。

     

             你说的问题,现在有什么好的解决办法么?

             我尝试过你说的重写(2. 如果我在普通工程下添加WCF服务引用,生成的可同步调用的客户端Proxy代码,稍作修改后可以在SilverLight工程中调用吗?), 发现没有用,这个是因为silver light所引用的service model 的版本和.net framwork3.0、3.5中的不是一个版本,而且其里面request都是异步的。后来,我把C:\Documents and Settings\liudi\Desktop\back\Reference Assemblies里的assembly强制换成3.0的,但是,问题也有一堆,silver light 内部好像有对版本的检测,如果能绕过这个版本检查的话,我估计就很容易了。

             请问,你现在有啥好办法么?谢谢:)

    2008年8月26日 7:06
  • 如果只是为了调用可以这样处理:

    var NetCoreWC = new WebClient();
                NetCoreWC.OpenReadCompleted += delegate(object sender1, OpenReadCompletedEventArgs e1)
                {
                    Stream stream1 = e1.Result;
                    AssemblyPart assemblyPart1 = new AssemblyPart();
                    Assembly assembly1 = assemblyPart1.Load(stream1);
                    this.Types["NetCore"] = assembly1.GetType("CommunicateLayer.NetCore");
                    object TmpNetCore = assembly1.CreateInstance("CommunicateLayer.NetCore");
                    myNetCore = (ICommunicateCore)TmpNetCore;
                    ICommunicateCore myNC = myNetCore;
                    ServerConfig mySC = new ServerConfig();
                    myNC.ErrorHandle += new EventHandler<NetEvent>(myErrorHandle);
                    myNC.CloseHandle += new EventHandler<NetEvent>(myCloseHandle);
                    myNC.ReveiceHandle += new EventHandler<NetEvent>(myReveiceHandle);
                    myNC.Net_Connect(mySC, null);
                };
                NetCoreWC.OpenReadAsync(new Uri("MainDll/CommunicateCore.dll", UriKind.RelativeOrAbsolute));

    如果需要向里传递数据可以:

     public void StartUITask(object taskName, object[] parameters)
            {
                object UserToken = parameters;
                if ("LoadTaskPack".Equals(taskName))
                {
                    loadTaskPack((string)parameters[0]);
                }
                else if ("Login".Equals(taskName))
                {
                    #region 开始拉取Login任务的定义DLL
                    //Common.dll
                    var CommonWC = new WebClient();
                    CommonWC.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
                    {
                        object UserToken1 = e.UserState;
                        Stream stream = e.Result;
                        AssemblyPart assemblyPart = new AssemblyPart();
                        Assembly assembly = assemblyPart.Load(stream);
                        this.Assemblys["Common"] = assembly;
                        var IMWC = new WebClient();
                        IMWC.OpenReadCompleted += delegate(object sender1, OpenReadCompletedEventArgs e1)
                        {
                            object UserToken2 = e.UserState;
                            object[] parametersAll = (object[])UserToken2;
                            Stream IMstream = e1.Result;
                            AssemblyPart IMassemblyPart = new AssemblyPart();
                            Assembly IMassembly = IMassemblyPart.Load(IMstream);
                            this.Assemblys["Kernel"] = IMassembly;                       
                            this.Types["CTXCSProcessorBase"] = this.Assemblys["Kernel"].GetType("Kernel.CTXCSProcessorBase");
                            this.Types["MD5"] = this.Assemblys["Common"].GetType("Common.Crypt.MD5");
                            this.Types["TXIM"] = this.Assemblys["Kernel"].GetType("Kernel.TXIM");
                         
                            this.Types["ContactStatus"] = this.Assemblys["Kernel"].GetType("Kernel.ContactStatus");
                            int dwUin;
                            string stPwd;
                                  
                            BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static;
                            Binder binder = null;
                            var args = new Object[1] { stPwd };
                            object bufHashOne = this.Types["MD5"].InvokeMember("GetMd5ByteArray", flags, binder, null, args);
                            object tmpCTXCSProcessorBase = this.Types["CTXCSProcessorBase"].InvokeMember("GetIntance", flags, binder, null, new object[0]);
                            object testNet = tmpCTXCSProcessorBase.GetType().InvokeMember("SetCommunicateCore", flags | BindingFlags.Instance, binder, tmpCTXCSProcessorBase, new object[1] { myNetCore });
                            if (!(bool)testNet)
                            {
                                return;
                            }
                            //object MyContactStatus = this.Assemblys["Kernel"].CreateInstance("Kernel.ContactStatus");
                            byte[] mybufHashOne = (byte[])bufHashOne;
                            object myFieldInfo = this.Types["ContactStatus"].InvokeMember("AVAILABLE", BindingFlags.Public | BindingFlags.GetField | BindingFlags.Static, null, null, new object[0]);
                            //enum tmpField = ((enum)(myFieldInfo));
                            //LoginResultHandler

                            object im = this.Assemblys["Kernel"].CreateInstance("Kernel.TXIM");
                            EventInfo LoginResultHandler = this.Types["TXIM"].GetEvent("LoginResultHandler");
                            MethodInfo eventHandler = this.GetType().GetMethod("OnMyLoginResult");

                            Delegate d = Delegate.CreateDelegate(LoginResultHandler.EventHandlerType, this, "OnMyLoginResult");
                            LoginResultHandler.AddEventHandler(im,d);
                            object LoginStart = this.Types["TXIM"].InvokeMember("Login", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, im, new object[4] { dwUin, mybufHashOne, (short)myFieldInfo, "I am OnLine" });
                            //this.Types["ITXCSSender"] myITXCSSender = (this.Types["ITXCSSender"])tmpCTXCSProcessorBase;

                        };
                        IMWC.OpenReadAsync(new Uri("MainDll/IM.dll", UriKind.RelativeOrAbsolute), UserToken1);
                    };
                    CommonWC.OpenReadAsync(new Uri("MainDll/Common.dll", UriKind.RelativeOrAbsolute), UserToken);
            }

    }

     

    这里两段代码算是模拟了同步,如果不这样简写,可以采用自定义事件方式处理,总之,模拟同步的思路就是:

    1、异步操作不可预知所以需要通知机制;

    2、事件是最好的通知机制,其次可以采用轮询和信号灯方式处理;

    3、模拟同步其实就需要发起有前置任务的时候收到前置任务完成情况的响应和通知,采用回调的方式也可以实现,就是说发起一个异步操作,传入回调函数句柄,在异步操作完成后调用回调函数执行后置操作。

     

    2008年9月9日 3:45