Silverlight 4.0和WCF客户端代理 - 如何创建以及如何关闭实例

时间:2011-08-19 08:01:02

标签: .net silverlight wcf silverlight-4.0 wcf-proxy

Silverlight WCF服务代理生命周期的主题对我来说不是很清楚。我在这里阅读了各种材料,资源和答案,但我仍然不完全理解使用它们的最佳方法。

我目前在Silverlight 4.0中使用自定义二进制绑定。

在silverlight中创建代理是一项昂贵的操作吗? 我们应该尝试在代码中共享代理实例还是创建新代表更好? 如果我们分享以防多个线程访问它,我们应该锁定吗?

由于代理上的错误会导致代理状态出错,我认为共享代理不是一个好主意,但我读过创建很昂贵,所以它不是100%明白这里要做什么。

关闭 - silverlight WCF服务客户端仅提供CloseAsync方法。代理还需要在关闭时使用某些逻辑(如果它们出现故障我们应该调用在Silverlight中同步的Abort(),如果不是,我们应该调用非同步的CloseAsync或什么?)。

在许多官方版本的Silverlight样本中,MS代理的样本都没有被关闭,这只是材料的缺陷或预期的方法吗?

主题对我来说非常重要,我希望清楚地了解我目前没有应该考虑的所有事情。

(我确实看到这个问题What is the proper life-cycle of a WCF service client proxy in Silverlight 3?看起来很接近我,但我不能说我对答案的质量感到满意)

我真的希望看到使用,创建,关闭等WCF代理的示例代码,最重要的是解释为什么这是最好的方法。我还认为(目前认为)由于问题的本质,应该有一个单一的,通用的最佳实践/模式 - 在Silverlight中使用(创建,重用,关闭)WCF代理的方法。

2 个答案:

答案 0 :(得分:8)

摘要:我认为最佳做法是在您即将使用它时实例化您的Web服务客户端,然后让它超出范围并获取垃圾。这反映在您看到的来自Microsoft的示例中。理由如下......

完整:我找到的最佳完整描述是How to: Access a Service from Silverlight。这里的示例显示了实例化Web服务客户端并允许它超出范围(不需要关闭它)的典型模式。 Web服务客户端继承自ClientBase,ClientBase具有Finalize方法,当对象被垃圾回收时,该方法应该在必要时释放任何非托管资源。

我有很多使用Web服务的经验,我使用代理并在使用前实例化它们,然后允许它们被垃圾收集。我从来没有遇到过这种方法的问题。我在Wenlong Dong's Blog上读到,其中说代理的创建是昂贵的,但即使他说.NET 3.5的性能也有所提高(也许它从那时起又有所改善?)。我可以告诉您的是性能是一个相对术语,除非您检索的数据的大小不是很小,否则将花费大量时间来序列化/反序列化和传输,而不是创建连接。这当然是我的经验,你最好先在这些领域进行优化。

最后,由于我认为我的观点到目前为止可能不够,我写了一个快速测试。我使用随Visual Web Developer 2010 Express提供的模板创建了支持Silverlight的Web服务(使用名为DoWork()的默认void方法)。然后在我的示例Silverlight客户端中,我使用以下代码调用它:

int counter=0;
public void Test()
{
    ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
    client.DoWorkCompleted += (obj, args) => 
    { 
        counter++;
        if (counter > 9999)
        {
            for(int j=0;j<10;j++) GC.Collect();
            System.Windows.MessageBox.Show("Completed");
        }
    };
    client.DoWorkAsync();
}

然后我使用for(int i=0;i<10000;i++) Test();调用了Test方法并启动了应用程序。加载应用程序需要花费20多秒的时间。完成Web服务调用(全部10,000个)。在进行Web服务调用时,我看到进程的内存使用量超过了150MB,但是一旦调用完成并调用了GC.Collect(),内存使用量就会下降到不到这个数量的一半。它似乎不是一个完美的测试,它似乎向我确认没有内存泄漏,或者它可以忽略不计(考虑到使用单独的客户端实例调用10,000个Web服务调用可能并不常见)。它也是一个比保持代理对象更简单的模型,并且不得不担心它出错并且不得不重新打开它。

测试方法的理由: 我的测试集中在2个潜在问题上。一个是内存泄漏,另一个是处理器创建和销毁对象所花费的时间。我的建议是遵循提供类的公司(Microsoft)提供的示例是安全的。如果您担心网络效率,那么您的示例应该没有问题,因为正确创建/部署这些对象不会影响网络延迟。如果花费99%的时间是网络时间,那么在开发时间方面优化1%的理论改进可能是浪费的(假设甚至可以获得一个好处,我相信我的测试清楚地显示了很少/没有)。是的,网络呼叫是本地的,也就是说,在10,000次服务呼叫过程中,只需要花费大约20秒来等待对象。这表示在创建对象上花费的每个服务调用约2毫秒。关于调用Dispose的需要,我并不是要暗示你不应该调用它,只是因为它似乎没有必要。如果你忘记了(或者只是选择不这样做),我的测试让我相信在Finalize中为这些对象调用了Dispose。即便如此,自己调用Dispose可能会更有效,但效果仍然可以忽略不计。对于大多数软件开发而言,通过提出更高效的算法和数据结构,比通过解决这些问题(除非存在严重的内存泄漏)获得更多收益。如果您需要更高的效率,那么您可能不应该使用Web服务,因为有比基于XML的系统更有效的数据传输选项。

答案 1 :(得分:0)

相对于呼叫的往返,代理创建并不昂贵。我已经看过在你调用其他异步方法后立即调用CloseAsync的评论,但这似乎有缺陷。从理论上讲,close会进入挂起状态,并在其他异步调用结束后发生。在实践中,我已经看到调用以错误快速结束,然后CloseAsync本身出错,因为频道已经出现故障。

我所做的是创建一个通用Caller<TResult>类,它具有多个CallAsync泛型方法重载,接受参数和类型为Action<TResult>的回调。在幕后,Caller将汇总XxxCompleted事件,并检查完成的原因是否是由于错误。如果错误,它将Abort通道。如果没有错误,它将调用CloseAsync然后调用回调。这可以防止“愚蠢”错误,例如尝试在故障状态下使用通道。

以上所有假设使用create-proxy-make-call-discard-use-of-proxy模型。如果您必须快速连续拨打多个电话,您可以调整此方法来计算机上呼叫的数量,并在完成最后一个呼叫时,执行关闭或中止。

每次呼叫或每组相关呼叫的代理创建开销很可能是一个问题。只有在开销太高的情况下,我才会采用代理缓存或重用策略。请记住,您持有资源的时间越长,失败的可能性就越大。至少在我的经验中,生命周期越短,越好用户感知到的性能,因为你打扰他/她的次数越少,你就不需要昂贵的维护重试逻辑(或至少更少) ()),你更确信你不会通过准确地知道何时摆脱它们来泄露记忆或资源。

关于你关于不关闭的例子的问题......好吧,那些是例子,通常是一维的和不完整的,因为它们说明了一般用途,而不是所有关于你需要做什么的具体细节应用程序的生命周期。