| File: OperationContextAsyncFlowTests.cs | Web Access |
| Project: src\src\System.Private.ServiceModel\tests\Scenarios\Binding\Tcp\Binding.Tcp.IntegrationTests.csproj (Binding.Tcp.IntegrationTests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Runtime.ExceptionServices; using System.ServiceModel; using System.Threading; using System.Threading.Tasks; using Infrastructure.Common; using ScenarioTests.Common; using Xunit; public class OperationContextAsyncFlowTests { [WcfFact] public static async Task OperationContextScopeAsyncFlow() { // This test lives in the scenario tests as it needs a real channel to test OperationContext // even though nothing is being sent across the wire. ChannelFactory<IWcfService> factory = null; IWcfService serviceProxy = null; var exisitingSyncContext = SynchronizationContext.Current; try { SynchronizationContext.SetSynchronizationContext(ThreadHoppingSynchronizationContext.Instance); NetTcpBinding binding = new NetTcpBinding(); factory = new ChannelFactory<IWcfService>(binding, new EndpointAddress(Endpoints.Tcp_DefaultBinding_Address)); serviceProxy = factory.CreateChannel(); Assert.Null(OperationContext.Current); using (var scope = new OperationContextScope((IContextChannel)serviceProxy)) { Assert.NotNull(OperationContext.Current); var currentContext = OperationContext.Current; int currentThreadId = Thread.CurrentThread.ManagedThreadId; await Task.Yield(); Assert.NotEqual(currentThreadId, Thread.CurrentThread.ManagedThreadId); Assert.Equal(currentContext, OperationContext.Current); } ((IClientChannel)serviceProxy).Close(); factory.Close(); } finally { SynchronizationContext.SetSynchronizationContext(exisitingSyncContext); await Task.Yield(); // Hop back to original sync context ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory); } } [WcfFact] public static async Task OperationContextScopeNestedAsyncFlow() { // This test lives in the scenario tests as it needs a real channel to test OperationContext // even though nothing is being sent across the wire. ChannelFactory<IWcfService> factory = null; IWcfService serviceProxy = null; var exisitingSyncContext = SynchronizationContext.Current; try { SynchronizationContext.SetSynchronizationContext(ThreadHoppingSynchronizationContext.Instance); NetTcpBinding binding = new NetTcpBinding(); factory = new ChannelFactory<IWcfService>(binding, new EndpointAddress(Endpoints.Tcp_DefaultBinding_Address)); serviceProxy = factory.CreateChannel(); Assert.Null(OperationContext.Current); using (var scope = new OperationContextScope((IContextChannel)serviceProxy)) { Assert.NotNull(OperationContext.Current); var firstContext = OperationContext.Current; int currentThreadId = Thread.CurrentThread.ManagedThreadId; await Task.Yield(); Assert.NotEqual(currentThreadId, Thread.CurrentThread.ManagedThreadId); Assert.Equal(firstContext, OperationContext.Current); using (var scope2 = new OperationContextScope((IContextChannel)serviceProxy)) { Assert.NotEqual(firstContext, OperationContext.Current); var secondContext = OperationContext.Current; currentThreadId = Thread.CurrentThread.ManagedThreadId; await Task.Yield(); Assert.NotEqual(currentThreadId, Thread.CurrentThread.ManagedThreadId); Assert.Equal(secondContext, OperationContext.Current); } Assert.Equal(firstContext, OperationContext.Current); } Assert.Null(OperationContext.Current); ((IClientChannel)serviceProxy).Close(); factory.Close(); } finally { // *** ENSURE CLEANUP *** \\ SynchronizationContext.SetSynchronizationContext(exisitingSyncContext); await Task.Yield(); // Hop back to original sync context ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory); } } [WcfFact] public static async Task OperationContextCallbackAsyncFlow() { ChannelFactory<IDuplexChannelService> factory = null; IDuplexChannelService serviceProxy = null; Guid guid = Guid.NewGuid(); try { // *** SETUP *** \\ NetTcpBinding binding = new NetTcpBinding(); binding.Security.Mode = SecurityMode.None; DuplexChannelServiceCallback callbackService = new DuplexChannelServiceCallback(); InstanceContext context = new InstanceContext(callbackService); EndpointAddress endpointAddress = new EndpointAddress(Endpoints.Tcp_NoSecurity_DuplexCallback_Address); factory = new DuplexChannelFactory<IDuplexChannelService>(context, binding, endpointAddress); serviceProxy = factory.CreateChannel(); // *** EXECUTE *** \\ serviceProxy.Ping(guid); await callbackService.CallCompletedTask; // *** VALIDATE *** \\ if (callbackService.Exception != null) { ExceptionDispatchInfo.Capture(callbackService.Exception).Throw(); } // *** CLEANUP *** \\ ((ICommunicationObject)serviceProxy).Close(); factory.Close(); } finally { // *** ENSURE CLEANUP *** \\ ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory); } } public class DuplexChannelServiceCallback : IDuplexChannelCallback { private TaskCompletionSource<object> _tcs; public DuplexChannelServiceCallback() { _tcs = new TaskCompletionSource<object>(); } public Exception Exception { get; private set; } public Task CallCompletedTask => _tcs.Task; public async Task OnPingCallbackAsync(Guid guid) { var exisitingSyncContext = SynchronizationContext.Current; try { SynchronizationContext.SetSynchronizationContext(ThreadHoppingSynchronizationContext.Instance); var asyncFlowDisabled = AppContext.TryGetSwitch("System.ServiceModel.OperationContext.DisableAsyncFlow", out var enabled) && enabled; Assert.False(asyncFlowDisabled, "DisableAsyncFlow should not be set to true"); var opContext = OperationContext.Current; Assert.NotNull(opContext); int currentThreadId = Thread.CurrentThread.ManagedThreadId; await Task.Yield(); Assert.NotEqual(currentThreadId, Thread.CurrentThread.ManagedThreadId); Assert.Equal(opContext, OperationContext.Current); _tcs.TrySetResult(null); } catch (Exception e) { Exception = e; } finally { // *** ENSURE CLEANUP *** \\ SynchronizationContext.SetSynchronizationContext(exisitingSyncContext); await Task.Yield(); // Hop back to original sync context } } } [ServiceContract(CallbackContract = typeof(IDuplexChannelCallback))] public interface IDuplexChannelService { [OperationContract(IsOneWay = true)] void Ping(Guid guid); } public interface IDuplexChannelCallback { [OperationContract(IsOneWay = true)] Task OnPingCallbackAsync(Guid guid); } }