RobertC RobertC - 2 months ago 24
C# Question

Programmatically create WCF Client

I have a WCF Service running and a client that I have working however currently the I have referenced the service in my client.

I would like to create the client reference dynamically.

Here is my config file:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=e77a5e561934e089" requirePermission="false" />
</configSections>
<system.serviceModel>
<services>
<!-- This section is optional with the new configuration model
introduced in .NET Framework 4. -->
<service name="Service.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://xx.xx.xx.xx:20001/Service/service" />
</baseAddresses>
</host>
<!-- this endpoint is exposed at the base address provided by host: http://xx.xx.xx.xx:20001/Service/service -->
<endpoint address="" binding="wsHttpBinding" contract="Service.IReportData" bindingConfiguration="CustomBinding" />
<!-- the mex endpoint is exposed at http://xx.xx.xx.xx:20001/Service/service/mex -->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="CustomBinding" closeTimeout="00:02:00" openTimeout="00:02:00" receiveTimeout="00:10:00" sendTimeout="00:02:00" maxReceivedMessageSize="2147483647">
<security mode="None">
<transport clientCredentialType="None" />
<message establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CalculatorServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="True" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite.EF6" />
<add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
</DbProviderFactories>
</system.data>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
<provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
</providers>
</entityFramework>
</configuration>


I have been googling for about a week and found the following code snippet that I have been trying to use that I found on stackoverflow.

The link to the post where I found it is: Configure wcf service programmatically

public virtual ChannelFactory<T> Proxy<T>(string address)
{
//Validate Address
if (string.IsNullOrEmpty(address)) throw new ArgumentNullException("Address can not be null or empty.");
//Address
EndpointAddress endpointAddress = new EndpointAddress(address);

//Binding
WSHttpBinding wsHttpBinding = new WSHttpBinding(SecurityMode.None, false);
wsHttpBinding.OpenTimeout = wsHttpBinding.CloseTimeout = new TimeSpan(0, 1, 0);
wsHttpBinding.ReceiveTimeout = wsHttpBinding.SendTimeout = new TimeSpan(0, 10, 0);
wsHttpBinding.MaxReceivedMessageSize = wsHttpBinding.MaxBufferPoolSize = 2147483647;
wsHttpBinding.BypassProxyOnLocal = wsHttpBinding.AllowCookies = wsHttpBinding.TransactionFlow = false;
wsHttpBinding.MessageEncoding = WSMessageEncoding.Text;
wsHttpBinding.TextEncoding = Encoding.UTF8;
wsHttpBinding.UseDefaultWebProxy = true;
wsHttpBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
wsHttpBinding.ReaderQuotas = new XmlDictionaryReaderQuotas(); //ReaderQuotas, setting to Max
wsHttpBinding.ReaderQuotas.MaxArrayLength = wsHttpBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
wsHttpBinding.ReaderQuotas.MaxStringContentLength = wsHttpBinding.ReaderQuotas.MaxNameTableCharCount = 2147483647;
wsHttpBinding.ReaderQuotas.MaxDepth = 2147483647;

//Create the Proxy
ChannelFactory<T> proxy = new ChannelFactory<T>(wsHttpBinding, endpointAddress);

//Sets the MaxItemsInObjectGraph, so that client can receive large objects
foreach (var operation in proxy.Endpoint.Contract.Operations)
{
DataContractSerializerOperationBehavior operationBehavior = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
//If DataContractSerializerOperationBehavior is not present in the Behavior, then add
if (operationBehavior == null)
{
operationBehavior = new DataContractSerializerOperationBehavior(operation);
operation.Behaviors.Add(operationBehavior);
}
//IMPORTANT: As 'operationBehavior' is a reference, changing anything here will automatically update the value in list, so no need to add this behavior to behaviorlist
operationBehavior.MaxItemsInObjectGraph = 2147483647;
}
return proxy;
}


When I try to compile I get two errors:


Error 1 'Proxy<...>': cannot declare instance members in a static class


and


Error 3 The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)


What am I missing in order to use the ChannelFactory class?

EDIT:

I have changed ChannelFactory to ChannelFactory but now I receive the following error:


Error 1 'Proxy<...>': cannot declare instance members in a static class

Answer Source
Error   1   'Proxy<...>': cannot declare instance members in a static class

Look at the class that this method is inside and make sure it's not static.

Error   3   The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)

When you see a definition for a generic class like ChannelFactory<T> or List<T>, T is just a placeholder for the actual type, like List<string> or List<DateTime>. From looking at the code above I'm guessing it's ChannelFactory<IReportData>.


Here's a really simple client. I used the sample service that gets created whenever you make a new WCF application project.

public class MyServiceClient : ClientBase<IService>, IService
{
    public string GetData(int value)
    {
        return base.Channel.GetData(value);
    }

    public CompositeType GetDataUsingDataContract(CompositeType composite)
    {
        return base.Channel.GetDataUsingDataContract(composite);
    }
}

From the documentation for ChannelFactory:

The generic ChannelFactory class is used in advanced scenarios that require the creation of a channel factory that can be used to create more than one channel.

In other words, if you're just trying to create a client then you probably don't need to use ChannelFactory.


In practice, I use Castle Windsor WCF Integration Facility to create my WCF clients. That's only applicable if you're interested in using dependency injection and working with Castle Windsor. I highly recommend learning to work with it just because dependency injection is a useful practice. But then in addition to that you get the convenience of using it to create and dispose your clients.

The result is that your classes never "know" about the WCF service. They just know that they're using IReportData. They have no idea that the implementation of that interface is a WCF client. Behind the scenes Windsor is creating and disposing the client, and in most cases the only configuration is a one-line <endpoint> element in your app.config.

Here's the entry page for their documentation.