WCF facility policy doesn't work when using async methods
See original GitHub issueWhen 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:
- Created 9 years ago
- Comments:6
Top GitHub Comments
Ha! Never ever worked with it, somehow I managed to dodge the bullet myself 😂
Closing as abandoned.