Sha*_*aan 17 c# multithreading task-parallel-library silverlight-5.0 dynamics-crm-2011
这是我注意到的非常奇怪的事情.
我正在编写CRM 2011 Silverlight扩展,而且,在我的本地开发实例上一切正常.该应用程序使用OData进行通信,并使用System.Threading.Tasks.Task
很多来在后台执行所有操作(FromAsync
是一种祝福).
但是,我决定在CRM 2011 Online中测试我的应用程序,并且令我惊讶地发现它将不再起作用; 结束检索任务时,我会收到安全异常.
使用Fiddler,我发现CRM正试图将我重定向到Live登录页面,考虑到我已经登录,这个页面没有多大意义.
经过多次尝试后,我发现错误是因为我从与UI线程不同的线程访问该服务.
这是一个简单的例子:
//this will work
private void button1_Click(object sender, RoutedEventArgs e)
{
var query = ctx.AccountSet;
query.BeginExecute((result) =>
{
textBox1.Text = query.EndExecute(result).First().Name;
}, null);
}
//this will fail
private void button2_Click(object sender, RoutedEventArgs e)
{
System.Threading.Tasks.Task.Factory.StartNew(RestAsync);
}
void RestAsync()
{
var query = ctx.AccountSet;
var async = query.BeginExecute(null, null);
var task = System.Threading.Tasks.Task.Factory.FromAsync<Account>(async, (result) =>
{
return query.EndExecute(result).First(); // <- Exception thrown here
});
textBox1.Dispatcher.BeginInvoke(() =>
{
textBox1.Text = task.Result.Name;
});
}
Run Code Online (Sandbox Code Playgroud)
似乎很明显我缺少一些关于线程如何使用权限的基础知识.由于在我的情况下使用单独的线程是可取的,有没有办法"复制"权限/身份验证?也许是某种模仿?
编辑:如果其他人正在努力解决这个问题,Task
只要query.BeginExecute(null, null);
在UI线程上执行,就可以使用其他线程(或者视情况而定).您需要一种方法来检索返回IAsyncResult
到调用线程,但您可以使用a来执行此操作ManualResetEvent
.
但我仍然想知道为什么线程之间不共享darned权限/身份验证...
小智 2
我不太确定这是否有帮助。但我找到了 Jeffrey Richter 第 770 页的描述
\n\n“与控制台应用程序一样,ASP.NET Web Form 和 XML Web Service 应用程序允许任何线程做任何它想做的事情。当线程池线程开始处理客户端\xe2\x80\x99s\n请求时,它可以假定客户端\ xe2\x80\x99s 区域性 (System.Globalization.CultureInfo),允许 Web 服务器返回数字、日期和时间的区域性特定格式。5 此外,Web 服务器可以采用客户端\xe2\x80\ x99s 标识 (System.Security.Principal.\nIPrincipal),以便服务器只能访问允许客户端\n访问的资源。当线程池线程生成异步操作时,\n它将由另一个线程池线程完成\n ,它将处理异步操作的结果。\n当代表原始客户端请求执行此工作时,区域性\n和身份信息默认情况下不会\xe2\x80\x99 流到新的线程池线程,因此代表客户端完成的任何\n其他工作现在不使用客户端\xe2\x80\x99s 文化和身份\n信息。理想情况下,我们希望文化和身份信息流向仍在代表同一客户端工作的其他线程\n池线程。”
\n\n这是他的例子,我希望这会有所帮助。
\n\nprivate static AsyncCallback SyncContextCallback(AsyncCallback callback) \n{\n SynchronizationContext sc = SynchronizationContext.Current;\n // If there is no SC, just return what was passed in\n if (sc == null) return callback;\n // Return a delegate that, when invoked, posts to the captured SC a method that\n // calls the original AsyncCallback passing it the IAsyncResult argument\n return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);\n}\n\nprotected override void OnMouseClick(MouseEventArgs e) {\n // The GUI thread initiates the asynchronous Web request\n Text = "Web request initiated";\n var webRequest = WebRequest.Create("http://Wintellect.com/");\n webRequest.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webRequest);\n base.OnMouseClick(e);\n}\n\nprivate void ProcessWebResponse(IAsyncResult result) {\n // If we get here, this must be the GUI thread, it\'s OK to update the UI\n var webRequest = (WebRequest)result.AsyncState;\n using (var webResponse = webRequest.EndGetResponse(result)) {\n Text = "Content length: " + webResponse.ContentLength;\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n这是我在我的应用程序中使用的
\n\n public override void UpdateCanvas(object parameter)\n {\n Action<GraphPane> startToUpdate = StartToUpdate;\n GraphPane selectedPane = Canvas.HostingPane.PaneList.Find(p => p.Title.Text.Equals(defaultPanTitle));\n startToUpdate.BeginInvoke(selectedPane, FormSyncContext.SyncContextCallback(RefreshCanvas), selectedPane);\n }\n\n public static AsyncCallback SyncContextCallback(AsyncCallback callback)\n {\n // Capture the calling thread\'s SynchronizationContext-derived object\n SynchronizationContext sc = SynchronizationContext.Current;\n\n // If there is no SC, just return what was passed in\n if (sc == null) return callback;\n\n // Return a delegate that, when invoked, posts to the captured SC a method that\n // calls the original AsyncCallback passing it the IAsyncResult argument\n return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);\n }\n
Run Code Online (Sandbox Code Playgroud)\n