升级.NET后未调用WCF异步'结束'方法

时间:2017-11-01 14:12:28

标签: c# .net wcf upgrade

我们有一个用.NET 3.5编写的应用程序(控制台应用程序“服务器”自托管一堆WCF服务和一些WPF客户端)。我想将“服务器”应用程序升级到.NET 4.6。为了进行测试,我只是要更改运行时并在4.6中添加一些子项目,其余项目为3.5。在顶级项目中,我将目标更改为4.6,并确保app.config文件中包含此内容:

<startup useLegacyV2RuntimeActivationPolicy="true">
  <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
</startup>

WCF服务和其他支持项目都在解决方案中,无论如何我都没有对它们进行修改。

我也没有修改WPF客户端。

在我们的一些服务中,我们在服务器上实现异步开始/结束模式 。这对我来说是新的,因为我使用async / await模式学习了WCF(虽然我一般都熟悉begin / end)。客户端上的任何异步要求都由调用代码决定。

服务

public interface IMyServiceCallback
{
    void UnrelatedNotifyClientsMethod(Notification message);
}



[ServiceContract(CallbackContract = typeof(IMyServiceCallback))]
public interface IMyService
{
    // ...

    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginFetchSomething(int howMany, AsyncCallback callback, object state);
    FetchSomethingResult EndFetchSomething(IAsyncResult result);

    // ...
}



[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyService : IMyService
{
    // ...

    [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
    public IAsyncResult BeginFetchSomething(int howMany, AsyncCallback callback, object state)
    {
        AsyncResult<FetchSomethingResult> something = new AsyncResult<FetchSomethingResult>(callback, state);

        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += new DoWorkEventHandler((sender, args) =>
        {
            try
            {
                FetchSomethingResult resultData = Database.FetchSomethingQuery(howMany);
                something.Result = resultData;
                something.Complete(true);
            }
            catch (Exception e)
            {
                Log(e);
                something.HandleException(e, false);
            }
            backgroundWorker.Dispose();
        });

        backgroundWorker.RunWorkerAsync();
        return something;
    }


    public FetchSomethingResult EndFetchSomething(IAsyncResult result)
    {
        AsyncResult<FetchSomethingResult> something = result as AsyncResult<FetchSomethingResult>;
        something.AsyncWaitHandle.WaitOne();
        return something.Result;
    }

    // ...

    // other similar methods

    // ...

}



public class AsyncResult : IAsyncResult
{

    // ...

    public void Complete(bool completedSynchronously)
    {
        lock (this.Mutex)
        {
            this.IsCompleted = true;
            this.CompletedSynchronously = completedSynchronously;
        }
        this.SignalCompletion();
    }

    protected void SignalCompletion()
    {
        (this.AsyncWaitHandle as ManualResetEvent).Set();
        ThreadPool.QueueUserWorkItem(d => { this.InvokeCallback(); });
    }

    protected void InvokeCallback()
    {
        if (this.Callback != null)
        {
            this.Callback(this);
        }
    }

    public void HandleException(Exception e, bool completedSynchronously)
    {
        lock (this.Mutex)
        {
            this.IsCompleted = true;
            this.CompletedSynchronously = completedSynchronously;
            this.Exception = e;
        }
        this.SignalCompletion();
    }

    // ...
}

客户端(由Visual Studio“添加服务参考”生成)

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class MyServiceClient : System.ServiceModel.DuplexClientBase<NameSpace.ServiceClients.IMyServiceClient>, NameSpace.ServiceClients.IMyServiceCliene
{
    // ...

    public NameSpace.ServiceClients.FetchSomethingResult FetchSomething(int howMany)
    {
        return base.Channel.FetchSomething(howMany);
    }

    // ...

}

在客户端上,我们有一些通用类来包装和公开我们的服务,调用可以在主线程上进行,但通常是通过后台工作者在后台线程上进行的。只需将服务器应用程序从.NET 3.5升级到4+并且不进行任何更改,就不再调用服务器上的End方法。我已经确认Begin方法返回,并且worker调用.Complete()并调用回调,但之后没有任何反应。 1分钟后,客户端将在呼叫时抛出超时异常。

我们的代码库相当庞大且复杂,在阅读.NET 4迁移说明后,我并未预料到行为会有任何变化。

编辑:我已经在微软服务跟踪实用程序的屏幕截图中显示了在更新(top,3.5)之前和之后(bottom,4.6)对FetchProductVersion的相同调用。

WCF async pattern end method not called

编辑2/3:在某些情况下,失败似乎不一致。

2 个答案:

答案 0 :(得分:3)

您必须正确设置IAsyncResult.CompletedSynchronously属性为false,因为您以异步方式完成IAsyncResult。与.NET Framework 4.6中的WCF相反,.NET Framework 3.5中的WCF在这种情况下忽略它。

所以将something.Complete(true);更改为something.Complete(completedSynchronously: false);

您可以通过在其getter中放置一个断点来检查使用IAsyncResult.CompletedSynchronously的人和时间:

bool _completedSynchronously = false;
public bool CompletedSynchronously
{
    get
    {
        // Set breakpoint here.
        return _completedSynchronously;
    }
    set
    {
        _completedSynchronously = value;
    }
}

如果使用.NET Framework 3.5启动WCF服务,您会注意到它实际上从未被调用过。所以WCF的旧版本只是忽略它。如果使用.NET Framework 4.6启动服务,您将看到WCF内部类DispatchOperationRuntime使用了很多服务。特别是InvokeBegin()InvokeCallback()函数。

答案 1 :(得分:1)

在深入挖掘之前,我会看到两个悬而未决的项目。

  1. 验证async开始和结束方法具有相同的后缀名称。
  2.   

    WCF自动将入站邮件路由到Begin&lt;方法名&GT;方法和路由结束&lt;方法名&GT;拨打出站信息。

    在您的伪代码中,它们不遵循上述命名约定:

    private void yourDragEnterHandler(object sender, DragEventArgs e) 
    {
        e.Effect = DragDropEffects.Move;
    }
    
    1. 验证您正在适当地装饰您的方法。 MSDN提供以下示例:

      IAsyncResult BeginFetchSomething(int howMany, AsyncCallback callback, object state);
      FetchSomethingResult EndFetchJobResults(IAsyncResult result);
      
    2. 在伪代码中,缺少OperationContract属性。希望这很简单。祝你好运!

      MSDN Source