Change defaults for runtime-constructed WCF bindings via config file - c#

We have a WCF client application that talks to a series of different service endpoints that all expose the same contract (basically, proxy services for disparate backends). Because we don't know the endpoint URLs until run-time, we're creating the service proxy classes dynamically like this:
public Factory()
{
this.ProxyFactory = new ChannelFactory<IProxyServiceChannel>(new BasicHttpBinding());
}
public IProxyServiceChannel GetProxy(Uri uri)
{
return this.ProxyFactory.CreateChannel(new EndpointAddress(uri));
}
This works fine, except in certain cases where the proxy is sending data that's larger than the default maximum received side of 64k. Normally, we'd just go into the configuration file and change the basic HTTP binding to allow bigger messages. With WCF 4.0 and up, we usually do this on the default (unnamed) binding, like so:
<basicHttpBinding>
<binding maxReceivedMessageSize="2147483647"
sendTimeout="00:10:00"
receiveTimeout="00:10:00">
<readerQuotas maxArrayLength="2147483647" />
</binding>
</basicHttpBinding>
Changing the default binding settings like this works for any services that we host in our application, as well as any service proxies that are created via "New Service Reference...". But for some reason, the new BasicHttpBinding() object we're supplying to our channel factory is not picking up these settings, and is falling back to the built-in defaults.
I had assumed that these binding settings would apply to any endpoint using a BasicHttpBinding that didn't have a more specific binding configuration specified. Is that correct? And if not, is there a way to get WCF to pick up these defaults automatically?
(NOTE: I know I can specify a name for the binding and use that in code, which is the option I'm currently pursuing, but for various technical reasons I'd prefer not to have to do that.)

new BasicHttpBinding() will give you an instance of BasicHttpBinding with the default values - I don't think it will read anything from the config file (even a default configuration) unless a configuration section name is passed in (which doesn't help for a default binding defined in the config file). What you want to do is create the instance and pass that in to the ChannelFactory. Something like this:
BasicHttpBinding binding = new BasicHttpBinding();
binding.MaxReceivedMessageSize = Int32.Max;
binding.SendTimeOut = new Timespan();
binding.ReceiveTimeout = new TimeSpan();
XmlDictionaryReaderQuotas quotas = new XmlDictionaryReaderQuotas();
quotas.MaxArrayLength = Int32.Max;
binding.ReaderQuotas = quotas;
this.ProxyFactory = new ChannelFactory<IProxyServiceChannel>(binding);
You could refactor the above so that you could generate via code any number of binding configurations (i.e., one method per binding configuration, for example).

Related

Communicating with containerized WCF service in Service fabric

I have a container running in SF. A WCF service is running inside the container on port 7777 and is bind to the host on the same port.
I have written a second SF service and deployed it on the cluster.
I have used the WCFComuunicationClientFactory to talk to the WCF service running inside the container.
However, I am getting an exception of type: EndPointNotFoundException on uri:
net.tcp://MyPc.XXX.com:7777/.
This makes sense since the contracts are exposed this way: net.tcp://MyPc.XXX.com:7777/ContractType.
How can I make it work?
I am attaching the code of the service trying to contact the container:
var wcfClientFactory = new WcfCommunicationClientFactory<MyContractService>(
clientBinding: binding,
servicePartitionResolver: partitionResolver
);
var newClient = wcfClientFactory.GetClientAsync(
new Uri("fabric:/MyContainerService"),
ServicePartitionKey.Singleton,
TargetReplicaSelector.Default,
null,
new OperationRetrySettings(),
CancellationToken.None
).Result;
var x = newClient.Channel.GetX();
when Accessing 'GetX' I get the endpoint exception.
The solution is inside the container endpoint definition in the service fabric serviceManifest.xml file.
It is possible to add a pathSuffix, like this:
<Endpoint Name="MyEndPoint" UriScheme="net.tcp" Port="3000" Protocol="tcp" PathSuffix="YourService" />
This will result in the following end point:
net.tcp://<host>:3000/YourService
Notice you can have as many as endpoints as you like, and in order to choose one from the client contacting the service inside the container, you should specify the parameter: 'listenerName' when creating 'ServicePartitionClient', which represents your EndPoint name.

ServiceModel.Discovery.DiscoveryClient(string endpointConfigurationName) throws ArgumentNullException

I have a small WCF Client which I'm trying to configure for service discovery via the application configuration file - however whenever I do this:
// Create a new DiscoveryClient instance from the 'DiscoveryEndpoint'
// configuration in App.config
DiscoveryClient discoveryClient = new DiscoveryClient("DiscoveryEndpoint");
I get an ArgumentNullException, Value cannot be null. Parameter name: contract. There is no contract parameter for this overload of the DiscoveryClient constructor, and the contract is correctly specified in the App.config (see below).
Here are the relevant sections of the App.config:
<system.serviceModel>
<client>
<endpoint name="DiscoveryEndpoint"
contract="IExampleContract"
kind="dynamicEndpoint"
endpointConfiguration="DynamicEndpointConfiguration"/>
</client>
<standardEndpoints>
<dynamicEndpoint>
<standardEndpoint name="DynamicEndpointConfiguration">
<discoveryClientSettings>
<endpoint kind="udpDiscoveryEndpoint"/>
<findCriteria duration="00:00:02">
<types>
<add name="IExampleContract"/>
</types>
<scopes>
<add scope="urn://wcf.test.com/examples/exampleContract/development"/>
</scopes>
</findCriteria>
</discoveryClientSettings>
</standardEndpoint>
</dynamicEndpoint>
</standardEndpoints>
</system.serviceModel>
This is targetted at .NET Framework 4.0, using Visual Studio 2010 SP1.
The documentation for this overload of the DiscoveryClient(string) constructor indicates that this should create a new DiscoveryClient instance with the configuration identified in App.config.
Has anyone else encountered this behaviour, and if so how did you resolve it?
The "DiscoveryEndpoint" that you defined in the config file is actually a service client end point, not the DiscoveryClient end point.
The following should work:
var exampleContractChannelFactory = new ChannelFactory<IExampleContract>("DiscoveryEndpoint");
var exampleContractClient = exampleContractChannelFactory.CreateChannel();
// You can now invoke methods on the exampleContractClient
// The actual service endpoint used by the client will be looked up dynamically
// by the proxy using the DiscoveryClient class internally.
OK, I've spent a lot of time in the debugger with the Enable .NET Framework source stepping option enabled, and I've found that the cause of this exception being thrown is (probably) a bug in the implementation of the way that the DiscoveryClient is instantiated from the configuration file - there is a call way down the call stack which passes a hard-coded value of null into a contract parameter, which is where the originating exception is thrown.
So, after much head-scratching, and lots of searching, I've come up with the following workaround (well, more like a total HACK:!) - which I'm posting here to help out anyone else who might encounter the same issue.
// HACK: The following is a workaround for a bug in .NET Framework 4.0
// Discovery should be possible when setup from the App.config with the
// following three lines of code:
//
// discoveryClient = new DiscoveryClient("DiscoveryEndpoint");
// Collection<EndpointDiscoveryMetadata> serviceCollection = discoveryClient.Find(new FindCriteria(typeof(IExampleContract))).Endpoints;
// discoveryClient.Close();
//
// However, a bug in the Discovery Client implementation results in an
// ArgumentNullException when running discovery in this way.
//
// The following code overcomes this limitation by manually parsing the
// standard WCF configuration sections of App.config, and then configuring
// the appropriate FindCriteria for a programmatically configured discovery
// cycle. This code can be replaced by the above three lines when either
// 1. The bug in .NET Framework 4.0 is resolved (unlikely), or
// 2. The application is retargeted to .NET Framework 4.5 / 4.5.1
//
// To aid future developers, this HACK will be extensively documented
// Load the App.config file into a ConfigurationManager instance and load the configuration
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Get the ServiceModel configuration group
ServiceModelSectionGroup serviceModelGroup = ServiceModelSectionGroup.GetSectionGroup(configuration);
// Get the StandardEndpoints configuration node
StandardEndpointsSection section = serviceModelGroup.StandardEndpoints;
// Get the DynamicEndpoint configuration node
Configuration dynamicEndpointConfiguration = section["dynamicEndpoint"].CurrentConfiguration;
// Get the first DynamicEndpoint configuration
// HACK: This assumes only one DynamicEndpoint configuration exists
// No additional configurations will be interpreted. This should
// not pose a problem as typically a client will only access a
// single service instance. This can be extended if necessary
// at a later time.
DynamicEndpointElement element = ((DynamicEndpointElement)serviceModelGroup.StandardEndpoints["dynamicEndpoint"].ConfiguredEndpoints[0]);
// Set the required Contract Type
// HACK: This is currently hard-coded to prevent the need to specify
// an AssemblyQualifiedName in the App.config file. This will
// not typically pose a problem as each client will typically
// only open a single service exposing a single, well-known,
// Contract Type
FindCriteria criteria = new FindCriteria(typeof(IExampleContract));
// Add all required Scopes to the FindCriteria instance
foreach (ScopeElement scopeElement in element.DiscoveryClientSettings.FindCriteria.Scopes)
{
criteria.Scopes.Add(scopeElement.Scope);
}
// Get the Discovery Duration
criteria.Duration = element.DiscoveryClientSettings.FindCriteria.Duration;
// Create a new Discovery Client instance
DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
// Retrieve the matching Service Endpoints via Dynamic Search
Collection<EndpointDiscoveryMetadata> serviceCollection = discoveryClient.Find(criteria).Endpoints;
// Close the Discovery Client
discoveryClient.Close();
// HACK: END -- Process the results of Discovery
Note that I'm assuming that this issue is resolved in .NET Framework 4.5 / 4.5.1 - it may not be, but I cannot test that at the moment.
If anyone else has an alternative solution, more elegant or efficient than this, please post it to help others.

Could not find endpoint element with name 'xxxxx' and contract 'yyy' in the ServiceModel client configuration section

I generated a proxy via this command -
svcutil.exe /language:cs /out:generatedProxy.cs /config:app.config https://service100.emedny.org:9047/MHService?wsdl
and then copied the elements from the resulting app.config into the app.config file of an existing project.
When I try to access the client in that config file via-
MHSClient serviceProxy = new MHSClient("MHSPort");
it should reference the second client below:
<client>
<endpoint address="https://webservices.hmsa.com/EDI27X/cstc/Hipaa27XService.svc"
binding="customBinding"
bindingConfiguration="wsHttpEndpoint"
contract="HIPAA27XServiceContract"
name="wsHttpEndpoint" />
<endpoint address="https://12.23.28.113:9047/MHService"
binding="customBinding"
bindingConfiguration="MHService_MHSPort"
contract="MHS"
name="MHSPort" />
</client>
but instead I get the error;
Could not find endpoint element with name 'MHSPort' and contract 'MHS' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element.'
If I go to definition of MHSClient, it takes me to the proxy.cs file and this line;
public partial class MHSClient : System.ServiceModel.ClientBase, MHS
solved with the following-
endptAddress = new EndpointAddress(new Uri("uri"/xxxx"), EndpointIdentity.CreateDnsIdentity("xxxxxx"), addressHeaders);
MHSClient serviceProxy = new MHSClient(b, endptAddress);
solved with the following-
endptAddress = new EndpointAddress(new Uri("uri"/xxxx"), EndpointIdentity.CreateDnsIdentity("xxxxxx"), addressHeaders);
MHSClient serviceProxy = new MHSClient(b, endptAddress);
#Guanxi gave me the clue when asking about endpoint address from the config file. Once I created the endpoint address then I could instantiate/create the service using the correct overload;
var b = new CustomBinding() as the first argument and for the second argument,
the correct endpoint address.
complicated - WS-Security - IBM Websphere server interop <-> wcf client within the context of various .NET and Visual Studio implementations of web services... oh my
You probably need to set the ConfigurationName property of the ServiceContractAttribute above the service contract interface, ConfigurationName should match your contract name.
'VB.NET:
Imports System.ServiceModel
<ServiceContract([Namespace]:="http://yournamespace", ConfigurationName:="MHS")> _
Public Interface MHS
//C#:
using System.ServiceModel;
[ServiceContract(Namespace="http://yournamespace", ConfigurationName="MHS")]
public interface MHS
Look at the auto generated code here:
MSDN: How to: Create a Windows Communication Foundation Client
Also worth looking at:
MSDN: ServiceContractAttribute Class

How to make your Service-Reference proxy URL dynamic?

I have a web reference to web-service:
using (var client = new GetTemplateParamSoapClient("GetTemplateParamSoap"))
{
TemplateParamsKeyValue[] responsArray = client.GetTemplatesParamsPerId(
CtId, tempalteIds.ToArray());
foreach (var pair in responsArray)
{
string value = FetchTemplateValue(pair.Key, pair.Value);
TemplateComponentsData.Add(pair.Key, value);
}
}
Tried to change a web-reference url from c# code: as advice here:
1) http://www.codeproject.com/KB/XML/wsdldynamicurl.aspx
2) How to call a web service with a configurable URL
3) http://aspalliance.com/283_Setting_Web_Service_References_Dynamically
But I get symbol is missing when trying to do:
client.Url
In addition I couldn't find a property of "Url_behavior"
It sounds like you've already added the service reference, but here's a walkthrough on adding, updating and removing service references.
Once you've got one of those in your project, you can alter the endpoint URI with one of the constructor overloads, as John Saunders said above. To do this, you'll need to know the name of the endpoint in your config file. For instance, after you add your service you might have elements like this in your config file:
<endpoint address="http://bleh.com/services/servicename.asmx"
binding="basicHttpBinding" bindingConfiguration="ServiceNameSoap"
contract="ServiceReference1.ServiceNameSoap" name="ServiceNameSoap" />
Given that endpoint, you can change the address at runtime by using the following overload:
var proxy = new ServiceReference1.ServiceNameSoapClient("ServiceNameSoap",
"http://new-address.com/services/servicename.asmx");
You can also do it after construction, but that becomes a little bit harder. If you need to do so, see the documentation on the Endpoint property and the associated type ServiceEndpoint.

Create WCF service client with specified address without specifying configuration name

Is there a way to create an instance of a WCF service client in C# with a specified endpoint address without specifying a configuration name?
By default, clients have these constructors:
public ServiceClient()
public ServiceClient(string endpointConfigurationName)
public ServiceClient(string endpointConfigurationName, string remoteAddress)
Obviously, there is a default configuration, because of the first constructor. What I want is to only specify the 2nd parameter of the final constructor. Right now, I'm struggling through reading the configuration elements of using ConfigurationManager to figure it out, but it seems horribly cumbersome. Is there a cleaner way?
I prefer not to use the endpoint configuration in the .config file. I normally do something like this:
BasicHttpBinding basicbinding = new BasicHttpBinding();
basicbinding.SendTimeout = TIMEOUT;
basicbinding.OpenTimeout = TIMEOUT;
ServiceClient client = new ServiceClient(basicbinding, new EndpointAddress(new Uri("http://xxxxx")));
Your generated client should also have a constructor that looks like this:
public ServiceClient(
System.ServiceModel.Channels.Binding binding,
System.ServiceModel.EndpointAddress remoteAddress)
: base(binding, remoteAddress) {
}
You can call this one without an endpoint configuration.
If you want to actually just want to call a service without having to know everything there is to know about WCF services and configuration handling, in C # you can just do...
String url = "http:\\somehost:someport\\pathToSomeService";
EndpointAddress address = new EndpointAddress(url);
Binding binding = new BasicHttpBinding();
YourClient client = new YourClient(binding, address);
// Call your client methods
client.SomeMethod(parm1, parm2);
The above assumes you generated a service reference and does not require configuration information to exist anywhere, not in the generated service reference, not in the DLL and not in the executable. No configuration. None.
I use the above in a true standalone service proxy dll. It is standalone in the truest sense of the word as it is completely configurable with no dependence on the calling executable to provide anything.
Well, you could use the default constructor, but then you'd have to manually program in all of the configuration settings. By specifying the configuration name, the service client will automatically load the configuration in from the .config file, all you need to know is which configuration to use (you can have multiple, e.g. one for HTTP and another for Net.Tcp). The remoteAddress, of course, just tells WCF where to make the connection.
If you are having trouble configuring the client settings themselves, make sure you're using the WCF Service Configuration tool. It works for both the service config as well as the client config.

Categories

Resources