ChannelFactory bug with dynamic arguments

2019-03-27 05:02发布

问题:

This question is related to Bug in the dynamic language runtime in combination with IIS 7.5

ChannelFactory hangs if I provide it with a correctly typed dynamic object.

dynamic src = "MSFT";

var binding = new BasicHttpBinding();
var endpoint = new EndpointAddress("http://www.restfulwebservices.net/wcf/StockQuoteService.svc");
var channel = new ChannelFactory<IStockQuoteService>(binding, endpoint).CreateChannel();

// this will print just fine
Console.WriteLine(channel.GetStockQuote(src as string));

// this will print just fine
Console.WriteLine(new StockQuoteServiceClient().GetStockQuote(src));

// this will never print and the application will hang with no exceptions
Console.WriteLine(channel.GetStockQuote(src));
  • The service above is public, is not mine, and you can test this code yourself if you just add the service reference to the endpoint provided in the code;
  • StockQuoteServiceClient was created by the Add Service Reference menu item and takes dynamic objects just fine;
  • This magically doesn't happen when I launch the application with F5 on Debug, all lines print and the program exits correctly;
  • If I run it and then attach the debugger during execution I can see it hung on the call to channel.GetStockQuote(src);
  • If I leave it be, the program eats all my memory;
  • It only hangs when I use my own ChannelFactory with dynamic objects, as described in the comments.

Why my ChannelFactory hangs when it takes dynamic objects as parameters when the one created by Add Service Reference runs just fine?

回答1:

When you use the dynamic keyword, every code related to the dynamic variable will be compiled in run-time by the DLR. When you call a method using a dynamic variable, the actual method signature is unknown in compile time and also the method return type and everything related to it creating something Eric Lippert called "Dynamic Contagion":

"As I pointed out last time, when an argument of a call is dynamic then odds are pretty good that the compiler will classify the result of the call as dynamic as well; the taint spreads. In fact, when you use almost any operator on a dynamic expression, the result is of dynamic type, with a few exceptions. ("is" for example always returns a bool.) You can "cure" an expression to prevent it spreading dynamicism by casting it to object, or to whatever other non-dynamic type you'd like; casting dynamic to object is an identity conversion."

WCF internals uses a lot of interfaces and abstractions and there's a known DLR limitation regarding abstractions and interfaces where DLR doesn't resolve the correct type. (Also take a look at this SO discussion)

I was able to correctly invoke the ChannelFactory using reflection and casting the parameter to other types (and also trying to invoke the service using the wrong type). The problem must be DLR related.

I'm unable to debug the DLR compilation but the problem may be related to the "dynamic contagion" and the interface resolution bug. With "contagion" every part of the WCF invocation may be compiled at runtime and the type resolution bug may create some endles loops in some corner cases like an overrided method implementation that invokes the base method and the base class was wrongly resolved to the same child class.

Some WCF internals executes extra instructions when the debugger is attached (Debugger.IsAttached) that extra generally consists in asserts, checks and attributions. The extra instructions may provide some information that kills the "dynamic contagion" and avoids the bogus endless loop.