c# - 升级 .NET 后未调用 WCF 异步 'End' 方法

标签 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 客户端。

在我们的一些服务中,我们在服务器上实现了异步开始/结束模式。这对我来说是新的,因为我通过异步/等待模式学习了 WCF(尽管我通常熟悉开始/结束)。客户端上的任何异步要求都留给调用代码。

服务

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);
    }

    // ...

}

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

我们的代码库相当庞大且复杂,我没想到在阅读 .NET 4 迁移说明后行为会发生任何变化。

编辑:我提供了一张 Microsoft 服务跟踪实用程序的屏幕截图,显示了在更新之前(顶部,3.5)和更新之后(底部,4.6)对 FetchProductVersion 的相同调用。

WCF async pattern end method not called

编辑 2/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()特别是功能。

关于c# - 升级 .NET 后未调用 WCF 异步 'End' 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47056882/

相关文章:

.NET 优化的 Int32

c# - 验证网络凭据以访问客户端对象模型上的 SharePoint 站点

c# - 在 WCF 服务引用之间共享类型并仍然自动生成 INotifyPropertyChanged

c# - 单元测试中using语句的性能影响

c# - wcf 配置文件与代码配置的优缺点

c# - 可以以声明方式设置 TextBox 文本吗?

C# 打印 TableEntity 属性,但忽略具有 [IgnoreProperty] 属性的属性

c# - BitmapSource 到 BitmapImage

c# 获取迭代器在字典中的位置

c# - 确定 Neo4j 驱动程序和 session 对象的范围