Silverlight HttpWebRequest.Create hangs inside async block

断了今生、忘了曾经 提交于 2019-12-03 07:16:47

(Thanks for sending this to fsbugs, to force us to take a hard look.)

The problem is Async.RunSynchronously. When called on the UI thread, this blocks the UI thread. And it turns out that WebRequest.Create() on Silverlight dispatches to the UI thread. So it is a deadlock.

In general, try to avoid Async.RunSynchronously on Silverlight (or on any UI thread). You can use Async.StartImmediate in this example. Alternatively, I think you can call RunSynchronously from any background thread without issues. (I have not tried enough end-to-end Silverlight scenarios myself to offer more advice as yet. You might check out

Game programming in F# (with Silverlight and WPF)

F# and Silverlight

F# async on the client side

for a few short examples.)

(In retrospect, the F# design team thinks that we maybe should not have included Async.RunSynchronously in FSharp.Core for Silverlight; the method potentially violates the spirit of the platform (no blocking calls). It's possible we'll deprecate that method in future Silverlight releases. On the other hand, it still does have valid uses for CPU-intensive parallelism on Silverlight, e.g. running a bunch on (non-IO) code in parallel on background threads.)

Seems like a similar issue here - though no reference to silverlight (in fact it is a "windows service class"):

http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/10854fc4-2149-41e2-b315-c533586bb65d

I had similar problem. I was making a Silverlight MVVM ViewModel to bind data from web. Don Syme commented himself:

I’m not a data-binding expert, but I believe you can’t “hide” the asyncness of a view model like this for WPF and Silverlight. I think you would need to expose Task, Async or an observable collection. AFAIK the only way to get Silverlight and WPF to bind asynchronously to a property is if it is an observable collection.

Anyway, I installed F# Power Pack to get AsyncReadToEnd. It didn't solve the case... I added domains to trusted sites but it didn't help... Then I added a MySolution.Web -asp.net-site and clientaccesspolicy.xml. I don't know if those had any effect.

Now, with Async.StartImmediate I got web service call working:

let mutable callresult = ""
//let event = new Event<string>()
//let eventvalue = event.Publish
let internal fetch (url : Uri) trigger = 
    let req = WebRequest.CreateHttp url
    //req.CookieContainer <- new CookieContainer()
    let asynccall =
        async{
            try
                let! res = req.AsyncGetResponse() 
                use stream = res.GetResponseStream()
                use reader = new StreamReader(stream)
                let! txt = reader.AsyncReadToEnd()
                //event.Trigger(txt)
                callresult <- txt //I had some processing here...
                trigger "" |> ignore
            with
                | :? System.Exception as ex -> 
                    failwith(ex.ToString()) //just for debug
        }
    asynccall |> Async.StartImmediate

Now I will need my ViewModel to listen the mutable callresult. In your case you need also a crossdomain.xml to the server.

The trigger is needed to use the UI-thread:

let trigger _ = 
    let update _ = x.myViewModelProperty <- callresult
    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(new Action(update)) |> ignore
fetch serviceUrl trigger
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!