快速提问:我的winform应用程序(c#)中的一个表单对WCF服务进行异步调用以获取一些数据。如果表单在回调发生之前碰巧关闭,则会因访问已处置对象的错误而崩溃。检查/处理这种情况的正确方法是什么?错误发生在对方法的Invoke调用上,以更新我的表单,但我无法向下钻取到内部异常,因为它表示代码已经过优化。
守则:
public void RequestUserPhoto(int userID) { WCF.Service.BeginGetUserPhoto(userID, new AsyncCallback(GetUserPhotoCB), userID); }public void GetUserPhotoCB(IAsyncResult result) { var photo = WCF.Service.EndGetUserPhoto(result); int userID = (int)result.AsyncState; UpdateUserPhoto(userID, photo); } public delegate void UpdateUserPhotoDelegate(int userID, Binary photo); public void UpdateUserPhoto(int userID, Binary photo) { if (InvokeRequired) { var d = new UpdateUserPhotoDelegate(UpdateUserPhoto); Invoke(d, new object[] { userID, photo }); } else { if (photo != null) { var ms = new MemoryStream(photo.ToArray()); var bmp = new System.Drawing.Bitmap(ms); if (userID == theForm.AuthUserID) { pbMyPhoto.BackgroundImage = bmp; } else { pbPhoto.BackgroundImage = bmp; } } } }
更新:
我仍然不知道该怎么做。我真正需要的是一个设计模式,用于在异步调用可以返回之前从处理表单优雅的win表单中进行WCF异步服务调用。用户可以随时单击表单或任何表单上的X.问题比我上面显示的单个例子大得多。我的应用程序实际上会进行数百次WCF调用,而我正试图弄清楚如何优雅地处理这些调用,并在整个应用程序中以一致的方式处理。例如,如果我必须为每个WCF调用添加100行代码与ManualResetEvents或后台工作者,或互斥,或其他什么,只是为了它不会轰炸我的应用程序,这将带来很大的错误空间。我需要的是一种干净的方式来异步调用服务,然后在表单恰好关闭时将其转换为单向调用。换句话说,让服务完成运行,但我不关心结果是什么,并且不调用回调,因为它不再存在。
答案 0 :(得分:1)
public void UpdateUserPhoto(int userID, Binary photo)
{
if ( Disposed || !IsHandleCreated )
return;
if (InvokeRequired)
...
答案 1 :(得分:0)
您正在为回调提供一个地址,以便在操作完成时点击。当您关闭表单时,该地址无效,因此您收到此错误。在允许表单关闭之前,您需要建立一种确定您是否有未完成电话的方式。
我正在研究BackgroundWorker
class。我确信你仍然可以通过在回调被触发之前关闭你的表单来设置崩溃,但是使用BackgroundWorker
,你应该能够查询它的状态并更优雅地处理你的情况。
在异步通话仍处于活动状态时,您不应该让表单关闭。使用BackgroundWorker
,您可以轻松确定异步通话是否处于活动状态。
答案 2 :(得分:0)
我相信Form对象上有一个IsDisposed属性。您可以在调用Invoke之前检查该属性。
答案 3 :(得分:0)
创建一个标志,类似“IsWaiting”作为ManualResetEvent(甚至是一个简单的bool),将其设置为true,并在异步结果返回时将其设置为false。
在你的类dispose方法中检查标志,只有在标志清除后才处理对象。 (暂停,以防万一有错误)
答案 4 :(得分:0)
我对这个问题有很多好的评论,但是我在整个申请过程中都没有好的模式。我在这里发布了我最终做的答案,并希望它会帮助其他人。
自动生成的WCF代理使用Completed事件创建同步调用方法,使用开始/结束模式的异步方法和基于事件的委托。我在上面原始问题中发布的示例使用了Begin / End模式。问题是,当进行回调时,您将不得不进行一次Invoke来访问您的UI线程。如果那个UI线程不再存在(例如,用户关闭了窗口),那么你就遇到了问题。基于事件的新模式会自动返回到UI线程,从我的测试开始,我无法在服务完成之前关闭它。我猜代理是否足够聪明,如果内存地址不存在,则不能调用已完成的处理程序?或者它可能会挂起到处理程序的内存地址,因此它不会被垃圾回收?所以你要做的就是添加一个Completed事件处理程序,触发你的调用等,servicenameAsync(),然后等待它返回你的处理程序(在UI线程上)。我还要确保将已完成的处理程序包装在try / catch块中以处理ObjectDisposedExceptions(如果有的话)。
现在最重要的事情是:它不会崩溃。
一个问题(至少对我而言)......我使用单例模式来访问我的WCF服务。这样做的问题是它创建了一个对整个应用程序使用的WCF代理的静态引用。听起来很方便,但是当您在进行异步调用之前将事件处理程序附加到Completed事件时,那些可以复制,重复等等。每次调用时,除了一个之外还添加一个已完成的事件处理程序。已添加到您的静态WCF代理。为了解决这个问题,我开始为每个调用声明一个新的代理客户端,或者如果每个winform类没有为每个winform类进行多次调用。如果有人对此有任何意见或更好的方式,请告诉我!单例模式的最大问题是,如果同时打开多个窗口(不同的类),调用相同的异步方法,但是您希望它们返回到不同的已完成处理程序,则不能这样做。