Silverlight HttpWebRequest.Create зависает внутри асинхронного блока

Я пытаюсь прототипировать вызов Rpc на веб-сервер JBOSS из Silverlight (4). Я написал код и работает в консольном приложении, поэтому я знаю, что Jboss отвечает на веб-запрос. Портирование его на Silverlight 4 вызывает проблемы:

let uri =  new Uri(queryUrl)
// this is the line that hangs
let request : HttpWebRequest = downcast WebRequest.Create(uri)
request.Method <- httpMethod;
request.ContentType <- contentType 

Это может быть проблема с песочницей, так как мой silverlight отключается от моей файловой системы, а Uri - ссылка на localhost - хотя я даже не получаю исключения. Мысли?

спасибо


ОБНОВЛЕНИЕ 1

Я создал новый проект и портировал свой код, и теперь он работает; что-то должно быть неустойчивым с точки зрения интеграции F # Silverlight. По-прежнему хотелось бы поразмышлять над отладкой "висячего" веб-сайта в старой модели...


ОБНОВЛЕНИЕ 2

let uri = Uri("http://localhost./portal/main?isSecure=IbongAdarnaNiFranciscoBalagtas")
// this WebRequest.Create works fine
let req : HttpWebRequest = downcast WebRequest.Create(uri)

let Login = async {
    let uri =  new Uri("http://localhost/portal/main?isSecure=IbongAdarnaNiFranciscoBalagtas")
     // code hangs on this WebRequest.Create
     let request : HttpWebRequest = downcast WebRequest.Create(uri)
     return request
}
Login |> Async.RunSynchronously

Мне что-то не хватает; блок Async отлично работает в консольном приложении - не разрешено ли в приложении Silverlight?

Ответ 1

(Спасибо, что отправили это в fsbugs, чтобы заставить нас внимательно посмотреть.)

Проблема Async.RunSynchronously. При вызове потока пользовательского интерфейса это блокирует поток пользовательского интерфейса. И получается, что WebRequest.Create() в Silverlight отправляется в поток пользовательского интерфейса. Так что это тупик.

В общем, старайтесь избегать Async.RunSynchronously в Silverlight (или в любом потоке пользовательского интерфейса). Вы можете использовать Async.StartImmediate в этом примере. В качестве альтернативы, я думаю, вы можете вызвать RunSynchronously из любого фонового потока без проблем. (Я еще не пробовал достаточно сложных сквозных сценариев Silverlight, чтобы предложить еще несколько советов. Вы можете проверить

Игровое программирование в F # (с Silverlight и WPF)

F # и Silverlight

F # async на стороне клиента

для нескольких коротких примеров.)

(В ретроспективе команда разработчиков F # считает, что мы, возможно, не должны были включать Async.RunSynchronously в FSharp.Core для Silverlight, способ потенциально нарушает дух платформы (без блокирующих вызовов). Возможно, мы будем осуждать этот метод в будущих версиях Silverlight. С другой стороны, он по-прежнему имеет допустимые применения для интенсивного использования ресурсов parallelism для Silverlight, например, для запуска параллельного параллельного (не-IO) кода в фоновом потоке.)

Ответ 3

У меня была аналогичная проблема. Я делал Silverlight MVVM ViewModel для привязки данных из Интернета. Дон Симе прокомментировал себя:

Я не специалист по связыванию данных, но я верьте, что вы не можете "скрыть" асинхронность такой модели для WPF и Silverlight. Я думаю, вам нужно будет выставить задачу, Async или наблюдаемый сбор. AFAIK единственный способ заставить Silverlight и WPF связываться асинхронно к свойству, если оно является наблюдаемой коллекцией.

В любом случае, я установил F # Power Pack для получения AsyncReadToEnd. Это не решило дело... Я добавил домены на доверенные сайты, но это не помогло... Затем я добавил MySolution.Web -asp.net-сайт и clientaccesspolicy.xml. Я не знаю, имели ли они эффект.

Теперь, с Async.StartImmediate, я получил вызов веб-службы:

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

Теперь мне понадобится моя ViewModel для прослушивания изменяемого вызова. В вашем случае вам также нужен crossdomain.xml для сервера.

Триггер необходим для использования UI-потока:

let trigger _ = 
    let update _ = x.myViewModelProperty <- callresult
    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(new Action(update)) |> ignore
fetch serviceUrl trigger