wcf
wcf copied to clipboard
The `GetBindingForEndpoint` and `GetEndpointAddress` generated methods should be public instead of private
This is not a bug per se but rather an annoying behaviour of the svcutil
tool.
The svcutil
tool generates static methods to get Binding
and EndpointAddress
for a given client (GetBindingForEndpoint
+ GetEndpointAddress
+ optionally GetDefaultBinding
and GetDefaultEndpointAddress
). Unfortunately all those methods are private and as such can't be easily consumed. Here's an example of what is currently generated.
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.HelloEndpointPort))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.HelloEndpointPort))
{
return new System.ServiceModel.EndpointAddress("http://apps.learnwebservices.com/services/hello");
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
private static System.ServiceModel.Channels.Binding GetDefaultBinding()
{
return HelloEndpointClient.GetBindingForEndpoint(EndpointConfiguration.HelloEndpointPort);
}
private static System.ServiceModel.EndpointAddress GetDefaultEndpointAddress()
{
return HelloEndpointClient.GetEndpointAddress(EndpointConfiguration.HelloEndpointPort);
}
public enum EndpointConfiguration
{
HelloEndpointPort,
}
Let's say you want to tweak the default binding to change some timeouts (and possible some other settings). This is currently impossible to do because the generated methods are private.
var binding = HelloEndpointClient.GetDefaultBinding();
binding.OpenTimeout = TimeSpan.FromSeconds(5);
binding.CloseTimeout = TimeSpan.FromSeconds(6);
binding.SendTimeout = TimeSpan.FromSeconds(7);
binding.ReceiveTimeout = TimeSpan.FromSeconds(8);
await using var client = new HelloEndpointClient(binding, HelloEndpointClient.GetDefaultEndpointAddress());
The contents of those generated methods must be duplicated when it needs to be tweaked.
Making those static methods public instead private would solve this issue.
I have now opened pull request #5180 to address this issue.
We 100% agree that being able to modify the binding is a really useful mechanism. I'd like to propose a different way to achieve it which I think will be more powerful. What do you think about having a partial method which you can provide the implementation for through a partial class that gets called to modify the binding before it's used? It might be easier if I provide what that would look like:
Generated Code HelloServiceClient.cs
partial class HelloServiceClient
{
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.HelloEndpointPort))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
ConfigureBinding(ref result);
return result;
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
public static partial void ConfigureBinding(ref binding);
}
Partial class that your own in your project HelloServiceClientCustomizations.cs
partial class HelloServiceClient
{
public static void ConfigureBinding(ref binding)
{
binding.OpenTimeout = TimeSpan.FromSeconds(5);
}
}
The advantage of this approach is the code is more concise at the point of consumption. If you had 10 places in your code when you new up an instance, the approach of exposing the methods as public would require modify each call point. The approach above would cause it to be automatically applied every time. I don't see a problem with also making these things public, but I think the approach I just outlined should be the primary mechanism for customizing the binding so you don't pollute the point of consumption with knowledge of modifying the binding.
I've given this a bit more thought and you don't need to make it public to use it. You can do this instead:
partial internal class HelloServiceClient
{
public static Binding GetGeneratedBinding()
{
// Because we're a member of HelloServiceClient, we can access all private members
return GetDefaultBinding();
}
public static System.ServiceModel.EndpointAddress GetGeneratedEndpointAddress()
{
return GetDefaultEndpointAddress();
}
}
Then your application code would look like this:
var binding = HelloServiceClient.GetGeneratedBinding();
binding.OpenTimeout = TimeSpan.FromSeconds(5);
binding.CloseTimeout = TimeSpan.FromSeconds(6);
binding.SendTimeout = TimeSpan.FromSeconds(7);
binding.ReceiveTimeout = TimeSpan.FromSeconds(8);
await using var client = new HelloServiceClient(binding, HelloEndpointClient.GetGeneratedEndpointAddress());
That's a good point. My claim about it being impossible to do because the generated methods are private was wrong! I see two downsides to this approach, though.
- You have to manually create a partial class for each client generated with the
svcutil
tool. - You can't use
GetDefaultBinding
andGetDefaultEndpointAddress
method names, you have to choose something else. Granted, they could be exposed asDefaultBinding
andDefaultEndpointAddress
properties, which is prettier thanGetGeneratedBinding
andGetGeneratedEndpointAddress
in my humble opinion.
public partial class HelloEndpointClient
{
public static Binding DefaultBinding { get; } = GetDefaultBinding();
public static EndpointAddress DefaultEndpointAddress { get; } = GetDefaultEndpointAddress();
}
Looking at it, I still think it should not be necessary to write such code and that those methods should be exposed publicly.