question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

WCF facility policy doesn't work when using async methods

See original GitHub issue

When using BeginWcfCall(...) all the wcf policy that tries to catch exception doesn’t work, I’m still investigating, but it seems the same problem that you can have in every async Interceptor, because of course if the function is async it will exit soon as the thread/task is created and fired. I had the same problem with the task programming, where I’ve fixed checking if the invocation.ReturnValue is a Task, and if yes, attach a continuation to do what was need it from the interception, in that case simple logging.

In the meanwhile I’m investigating the issue, and a possible solution, I would like to share with you a test console program, that shows the behavior:

using System;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading;
using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.Windsor;

namespace TestClient
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.Write("Test sync (1 default) or async (2): ");
            var test = Console.ReadLine();

            var returnFaults = new ServiceDebugBehavior
            {
                IncludeExceptionDetailInFaults = true,
                HttpHelpPageEnabled = true
            };

            var serverContainer = new WindsorContainer()
                .AddFacility<WcfFacility>()
                .Register(
                    Component.For<IServiceBehavior>().Instance(returnFaults),
                    Component.For<ITestService>()
                             .ImplementedBy<TestService>()
                             .AsWcfService(new DefaultServiceModel()
                                               .AddBaseAddresses("http://localhost:4455")
                                               .PublishMetadata(mex => mex.EnableHttpGet())
                                               .AddEndpoints(WcfEndpoint
                                                                 .ForContract<ITestService>()
                                                                 .BoundTo(new BasicHttpBinding())))
                );

            var clientContainer = new WindsorContainer()
                .AddFacility<WcfFacility>()
                .Register(
                    Component.For<CatchAsyncPolicy>(),
                    Component.For<ITestService>()
                             .AsWcfClient(WcfEndpoint.ForContract<ITestService>()
                                                     .BoundTo(new BasicHttpBinding())
                                                     .At("http://localhost:4455"))
                );

            bool stopped = false;
            var testService = clientContainer.Resolve<ITestService>();
            var t = new Thread(() =>
            {
                while (!stopped)
                {
                    if (test == "2")
                    {
                        try
                        {
                            Console.WriteLine("Async Call: testService.BeginWcfCall(c => c.GetRandomValue(), call =>");
                            var ac = testService.BeginWcfCall(c => c.GetRandomValue(), call =>
                            {
                                try
                                {
                                    var rn = call.End();
                                    Console.WriteLine("Async Reply: random number: {0}", rn);
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine("Async Error: {0}", e.Message);
                                }
                            }, null);

                            ac.AsyncWaitHandle.WaitOne();
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("BeginWcfCall Error: {0}", ex.Message);
                        }
                    }
                    else
                    {
                        // test == "1"
                        try
                        {
                            Console.WriteLine("Call: testService.GetRandomValue()");
                            var rn = testService.GetRandomValue();
                            Console.WriteLine("Reply: random number: {0}", rn);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Error: {0}", ex.Message);
                        }
                    }

                    if (!stopped) Thread.Sleep(2000);
                }
            });
            t.Start();

            Console.WriteLine("Press enter to CLOSE the service...");
            Console.ReadLine();

            serverContainer.Dispose();

            Console.WriteLine("Press enter to exit the application...");
            Console.ReadLine();

            stopped = true;
            t.Join();

            clientContainer.Release(testService);
            clientContainer.Dispose();
        }
    }

    public class CatchAsyncPolicy : AbstractWcfPolicy
    {
        public override void Apply(WcfInvocation wcfInvocation)
        {
            var refresh = false;

            try
            {
                Console.WriteLine("Proceed: wcfInvocation.Refresh(false).Proceed()");
                wcfInvocation.Refresh(false).Proceed();
                //var result = invocation.ReturnValue;
                //var task = result as Task;
                //if (task != null)
                //{
                //}
            }
            catch (Exception ex) // Catch everything just for testing purpose
            {
                refresh = true;
                Console.WriteLine("Proceed Exception!");
            }

            if (refresh)
            {
                Console.WriteLine("Retry-Proceed: wcfInvocation.Refresh(true).Proceed()");
                wcfInvocation.Refresh(true).Proceed();
            }
        }
    }

    [ServiceContract]
    public interface ITestService
    {
        [OperationContract]
        int GetRandomValue();

        [OperationContract]
        int MultiplyValues(int a, int b);

        [OperationContract]
        void Execute();
    }

    public class TestService : ITestService
    {
        public int GetRandomValue()
        {
            var random = new Random();
            return random.Next();
        }

        public int MultiplyValues(int a, int b)
        {
            return a * b;
        }

        public void Execute()
        {
            Trace.WriteLine("Call Execute()");
        }
    }
}

The test start and ask which kind of test would you like to run: 1 for sync, 2 for async.

After the start, you should see calls and replies, then press ENTER to close the service and simulate a failure of the endpoint.

If you run the test in sync, you will see that the CatchAsyncPolicy will catch the exception and retry the call. If you run the test in async, instead you will see that no retry is going on and no exception is catch by around proceed.

Issue Analytics

  • State:closed
  • Created 9 years ago
  • Comments:6

github_iconTop GitHub Comments

1reaction
ghostcommented, Jul 18, 2018

Ha! Never ever worked with it, somehow I managed to dodge the bullet myself 😂

1reaction
ghostcommented, Jul 12, 2018

Closing as abandoned.

Read more comments on GitHub >

github_iconTop Results From Across the Web

WCF asynchronous call not returning data
I have a problem regarding an asynchronous call of a WCF web service method. It refuses to return the requested object when called...
Read more >
How to: Call WCF Service Operations Asynchronously - ...
Learn how to create a WCF client that can access a service operation asynchronously by using the event-driven asynchronous calling model.
Read more >
Synchronous and Asynchronous Operations - WCF
Many applications call methods asynchronously because it enables the application to continue doing useful work while the method call runs.
Read more >
Make your WCF Service async - Code Inside Blog
WCF services had always close relationships with their clients and so it is no suprise, that most guides show how to implement async...
Read more >
Doordash invalid authorization header found - Claras Dad
But some facilities of your server will not know that …The HTTP WWW-Authenticate response header defines the HTTP authentication methods ("challenges") ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found