【问题标题】:What do I need to do to prevent a client waiting on a long WCF service method?我需要做些什么来防止客户端等待长 WCF 服务方法?
【发布时间】:2014-08-14 18:42:03
【问题描述】:

我正在编写一个 WCF Web 服务,其中包含一个接受对象数组并将它们插入数据库的方法。这可能需要很长时间,所以我不能指望客户等待。

我的同事认为我不需要做任何事情,异步调用我的服务是客户的工作。我只是写一个普通的方法。这对我来说听起来不对,尽管我希望这是真的,因为到目前为止,查看 WCF 异步教程和 SO 问题让我感到困惑。

他说的对吗?如果不是,我该如何以允许客户端异步调用该方法或以其他方式避免挂起的方式实际编写该方法?

如果他是正确的(看起来是这样),那么定义异步方法有什么意义([OperationContract (AsyncPattern=true)]、Begin、End 等)。它是一种显式处理异步调用的方式,还是允许交互性,还是什么?

【问题讨论】:

  • 您的客户是正确的。服务没有什么特别的。
  • 是的,同事 100% 正确。

标签: c# wcf asynchronous import bulk


【解决方案1】:

应该落在客户端。他们是必须防止他们的应用程序/用户界面挂起的人。

让您的客户异步调用您的方法。如果他们使用服务引用,所有方法/事件都会自动生成。

myWcfClient.myMethodCompleted 
          += new EventHandler<myMethodCompletedEventArgs>(myCallBack);
myWcfClient.myMethodAsync(args);


public void myCallback(object sender, myMethodCompletedEventArgs e)
{
  var myResult = e.Result;

}

【讨论】:

  • 那么 AsyncPattern 有什么用呢?比简单导入需要更复杂的交互?
  • 如果他们在 Visual Studio 中生成服务引用,这些方法将已经自动生成。
  • @Drew AsyncPattern 是为了让您可以从 WCF 将任务/前体返回到任务,这是您不应该做的事情。
【解决方案2】:

如果您的客户不关心服务调用会发生什么,您需要一个简单的触发后忘记操作,您可以执行this

AsyncPattern 属性告诉运行时您的操作实现了 .NET Framework 异步方法设计模式。 See here。如果您希望您的客户端应用程序知道您的服务调用发生了什么,那么您可以使用此模式。不过,还有其他方法可以获得结果。

【讨论】:

    【解决方案3】:

    这只是在客户端,我已经跳过了旧的事件驱动异步 bleh 模式,并将其替换为异步等待模式。不等待 webmethod 调用异步,并阻塞 UI...甚至不属于本世纪 ​​;)

    如果您使用的是 .net 4.5+,您将免费获得 async-await 模式(除非 wp8,您仍然需要在其中包装它)。异步方法应该已经可以通过服务使用。如果您使用的是旧框架,我推荐AsyncBridge,它允许您在这种情况下使用异步等待模式。另一种选择是坚持旧的事件驱动的异步噩梦。以下示例只有在您使用 C#5.0 或从不使用时才可能

    确保从非异步方法在新线程中启动。

    Task.Factory.StartNew(client.DoSomethingAsync("blabla") ).ContinueWith(...);
    

    最后一部分在你的方法完成后运行,检查完成代码的异常等。

    或者在一些异步方法中

    public async Task<string> DoSomethingAsync(String text) {
       // Exception handling etc
       return await client.DoSomethingAsync(text);
    }
    

    将 APM 包装为异步等待模式:

    public class ServiceWrapper : IServiceWrapper 
    {
        readonly YourServiceClient client;
        public ServiceWrapper(YourServiceClient client)
        {
            this.client = client;
        }
    
        public async Task<string> DoSomethingAsync(string someParameter)
        {
            return await Task<string>.Factory.FromAsync(client.BeginDoSomeStuff, client.EndDoSomeStuff, someParameter, new object());
        }
    }
    

    编辑 在包装服务中打开和关闭连接。 (我现在没有可用的 devbox,但这应该可以)。

    public class ServiceWrapper : IServiceWrapper 
    {
        EndpointAddress address;
        public ServiceWrapper(EndpointAddress clientAddress)
        {
             address = clientAddress;
        }
    
        public async Task<string> DoSomethingAsync(string someParameter)
        {
            // handle exceptions etc here, can be done some cleaner..
            var client = new YourServiceClient();
            client.Endpoint.Address = address.Address; // can skip this..
            await client.OpenAsync()
            var res =  await Task<string>.Factory.FromAsync(client.BeginDoSomeStuff, client.EndDoSomeStuff, someParameter, new object());
            await client.CloseAsync();
            return res;
        }
    }
    

    最后一件事我不确定你是如何生成代理的,如果你使用 vs 确保在配置服务时勾选允许异步方法的复选框。如果您使用的是 svcutil,请添加适当的标志。

    Async-await pattern

    Old event driven async pattern

    希望对你有帮助,

    干杯,

    斯蒂安

    【讨论】:

    • 我开始使用类似的方法,但我的老板说这种方法会一直保持连接打开,他们不希望这样。这是真的吗?
    • @DrewS 不,这是错误的!好吧,上面的代码会做到这一点,我会修改它以每次连接。您只需打开连接,等待 wcf 调用然后关闭连接。为了简单起见,我没有添加它。
    • @DrewS 如果您查看最后一部分,我现在为您修改了代码。在这里,我每次执行 wcf 调用时都会打开和关闭一个连接。没有时间介绍错误处理,但有很多信息可以参考。帖子很长;)
    • 这段代码很糟糕。通信异常现在泄漏为未观察到的异常,不应使用 StartNew blog.stephencleary.com/2013/08/startnew-is-dangerous.html 也不应使用 ContinueWith blog.stephencleary.com/2013/10/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多