Keep WCF Client Configuration in a Class Library app.config
One way is:
Creating objects for the endpoint and Bindings below like this:
public static void ToGetServiceURL()
{
try
{
ServiceURL = Properties.Settings.Default.ServiceURL.ToString();
if(ServiceURL.Trim().Equals(String.Empty))
ServiceURL = "http://inadkotees1l1c/RPAWFMServices/RPAService.svc";
}
catch (Exception ex)
{
ServiceURL = "http://inadkotees1l1c/RPAWFMServices/RPAService.svc";
}
}
{
try
{
ServiceURL = Properties.Settings.Default.ServiceURL.ToString();
if(ServiceURL.Trim().Equals(String.Empty))
ServiceURL = "http://inadkotees1l1c/RPAWFMServices/RPAService.svc";
}
catch (Exception ex)
{
ServiceURL = "http://inadkotees1l1c/RPAWFMServices/RPAService.svc";
}
}
and
ToGetServiceURL();
EndpointAddress endpointAddress = new EndpointAddress(ServiceURL);
BasicHttpBinding serviceBinding = new BasicHttpBinding();
serviceBinding.ReceiveTimeout = new TimeSpan(0, 0, 120);
RPA_WFMServices.RPAServiceClient oRPAWFMService = new RPA_WFMServices.RPAServiceClient(serviceBinding,endpointAddress);
Result = oRPAWFMService.ToUpdateCaseStatus(ApplicationName, CaseID, URNID, QueueHistoryID, Action, Description, UserName, Password, DomainName, Appuserid, HostName);
EndpointAddress endpointAddress = new EndpointAddress(ServiceURL);
BasicHttpBinding serviceBinding = new BasicHttpBinding();
serviceBinding.ReceiveTimeout = new TimeSpan(0, 0, 120);
RPA_WFMServices.RPAServiceClient oRPAWFMService = new RPA_WFMServices.RPAServiceClient(serviceBinding,endpointAddress);
Result = oRPAWFMService.ToUpdateCaseStatus(ApplicationName, CaseID, URNID, QueueHistoryID, Action, Description, UserName, Password, DomainName, Appuserid, HostName);
On Another ways is: (Using .Net 4.0 - ConfigurationChannelFactory)
When building a WCF client with
Visual Studio with the simple “Add Service Reference” feature, you get your
client configuration (system.serviceModel section) in your app.config
file. If the project that holds the config file is an application (Winform,
WPF, ASP.NET etc.) then probably you’re just in the demo phase. Usually you
have several projects that need to call the service, and you don’t want to add
the service references in each. Also, you don’t want to bind all other project
to the WCF technology, so you want them to call a library that communicates
with the WCF service.
After creating you class library
with the service references and app.config file, you can create a layer over
the service calls like that:
//
Service code
[ServiceContract]
public
interface IService1
{
[OperationContract]
string GetData(int value);
}
//
Client code
public
class Service1Agent
{
public static int GetData(string value)
{
using (var client = new Service1Client())
{
return client.GetData(value);
}
}
}
Now any call to the WCF service is
unaware of the WCF technology:
var
result = Service1Agent.GetData(5);
When running this simple and
“obviously should work” code, you’ll get the following famous exception:
Could not find default endpoint
element that references contract ‘Service1.IService1′ 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
contract could be found in the client element.
The reason for that is simple. Class
libraries don’t have a configuration file of their own in run-time and they
rely on the configuration file of the app they run within. You end up copying
the system.serviceModel section to the app.config or web.config of your
application and also to your tests project (as said here and here). Although you might accept the duty to do
it every time your class library config file gets updated (adding service
reference, changing address, changing security etc.), there are scenarios this
is not acceptable, for example when you develop your ServiceAgent
project for an existing product that you’re not allowed to edit its application
config file.
Although this is a very famous
issue, there are not many solutions for that (except to copy). Possible
solution to the problem you can find here, however the code that
generates the client is very complicated. A better solution can be found here, but it assumes that
you know the endpoint name and it is hard-coded.
My solution is based on the latter,
but is dynamic. Microsoft added a new class in .NET 4 called ConfigurationChannelFactory. This class allows
you to create a channel with custom configuration source, for example:
var
channelFactory = new ConfigurationChannelFactory(endpointName, configuration,
null);
var
client = channelFactory.CreateChannel();
var
result = client.GetData(5);
For the above code to work, you need
to load your configuration and know the endpoint name. Loading the
configuration from file is easy with the ConfigurationManager.OpenMappedExeConfiguration
method:
var
configuration = ConfigurationManager.OpenMappedExeConfiguration(
new
ExeConfigurationFileMap
{
ExeConfigFilename = "app.config"
},
ConfigurationUserLevel.None);
The app.config file is the class
library config file. You can have it copied to the application output directory
by setting “Copy To Output Directory” to “Copy always” in the file properties.
As for the endpoint name, in order
not to make a switch-case for all service reference? you got, you need to find
it in the config file. In the config file the endpoint may appear like that:
Knowing the contract name can help
you find the endpoint name. The interface hierarchy in the generated client
code is:
With an instance of IService1Channel
you can call the service and close connection since it is IDisposable,
so this is the type you should pass as the generic T parameter to the ConfigurationChannelFactory
constructor.
The interface IService1 has
an attribute with the ConfigurationName that corresponds to the contract attribute
value in the config file:
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="Service1.IService1")]
public
interface IService1
{
}
In order to find this attribute we
can find the interface that IService1Channel inherits from and find the
attribute with GetCustomAttributes:
var
channelType = typeof(T);
var
contractType = channelType.GetInterfaces().First(i => i.Namespace ==
channelType.Namespace);
//
We compare the namespace because the inteface inherits also from
System.ServiceModel.IClientChannel
var
contractAttribute =
contractType.GetCustomAttributes(typeof(ServiceContractAttribute),
false).First() as ServiceContractAttribute;
With the configuration and the
contract name you can find the endpoint:
var
serviceModelSectionGroup =
ServiceModelSectionGroup.GetSectionGroup(configuration);
var
endpoint = serviceModelSectionGroup.Client.Endpoints.OfType().First(e =>
e.Contract == contractAttribute.ConfigurationName);
All above code is making the
complete client generation helper method:
public
static T GetClient() where T : IClientChannel
{
var channelType = typeof(T);
var contractType =
channelType.GetInterfaces().First(i => i.Namespace ==
channelType.Namespace);
var contractAttribute =
contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false)
.First() as ServiceContractAttribute;
var configuration =
ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap
{
ExeConfigFilename =
"app.config"
}, ConfigurationUserLevel.None);
var serviceModelSectionGroup =
ServiceModelSectionGroup.GetSectionGroup(configuration);
var endpoint =
serviceModelSectionGroup.Client.Endpoints.OfType()
.First(e = e.Contract
== contractAttribute.ConfigurationName);
var channelFactory = new
ConfigurationChannelFactory(endpoint.Name, configuration, null);
var client =
channelFactory.CreateChannel();
return client;
}
With this helper method you can
write your ServiceAgent like that:
public
static string GetData(int value)
{
using (var client = ClientHelper.GetClient())
{
return client.GetData(value);
}
}
With this code you have achieved
abstraction over the WCF technology and you no longer need to inject XML into
the application config file as long as you library app.config is in the
application bin folder.
- See more at:
http://www.tikalk.com/net/keep-wcf-client-configuration-class-library-appconfig/#sthash.Zp7J6oTb.dpuf



Read User's Comments