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