using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Description;
using System.ServiceModel;
using DynamicWCFFactory;
using System.Configuration;
using DynamicWCFFactory.Behaviors;
using Microsoft.Practices.EnterpriseLibrary.Logging;
namespace DynamicWCFFactory
{
/// <summary>
/// Discovers a service on its /mex or ?WSDL endpoint and creates a proxy
/// </summary>
/// <typeparam name="TClient">The type of the WCF client e.g. SCDServiceClient</typeparam>
/// <typeparam name="TServiceInterface">The ServiceInterface</typeparam>
public class DynamicWCFFactory<TClient, TServiceInterface>
where TClient : class
where TServiceInterface : class
{
/// <summary>
/// Region for the ErrorIDS
/// </summary>
#region ERROR_IDS
const int ERROR_MEX_FAILURE = 600;
const int ERROR_WSDL_FAILURE = 601;
const int ERROR_MDS_FAILURE = 602;
#endregion
#region Properties
public int BindingID
{ get; set; }
#endregion
#region PrivateMethods
/// <summary>
/// Instantiates a new Proxy by endpoint
/// </summary>
/// <typeparam name="ServiceType">The service client type</typeparam>
/// <typeparam name="ServiceInterface">Service Interface Type</typeparam>
/// <param name="CallbackInstance">Callback Instance for Duplex Bindings</param>
/// <param name="endpoint">The endpoint for the proxy</param>
/// <returns>An instance of ClientType</returns>
private static TClient ActivateProxy(object CallbackInstance, ServiceEndpoint endpoint)
{
if (typeof(TClient) is DuplexClientBase<TServiceInterface>) // DuplexClientBase
return Activator.CreateInstance(typeof(TClient), new object[] { new InstanceContext(CallbackInstance), endpoint.Binding, endpoint.Address }) as TClient;
else
return Activator.CreateInstance(typeof(TClient), new object[] { endpoint.Binding, endpoint.Address }) as TClient;
}
/// <summary>
/// Instantiates a new Proxy by Endpoint Adress and applies endpoint configuration from configuration file
/// </summary>
/// <typeparam name="ServiceType">The service client type</typeparam>
/// <typeparam name="ServiceInterface">Service Interface Type</typeparam>
/// <param name="CallbackInstance">Callback Instance for Duplex Bindings</param>
/// <param name="endPointConfig">The name for the endpoint configuration in config file</param>
/// <param name="endPointAdress">Address of service endpoint</param>
/// <returns></returns>
private static TClient ActivateProxy(object CallbackInstance, string endPointConfig, string endPointAdress)
{
if (typeof(TClient) is DuplexClientBase<TServiceInterface>) // DuplexClientBase
return Activator.CreateInstance(typeof(TClient), new object[] { new InstanceContext(CallbackInstance), endPointConfig, endPointAdress }) as TClient;
else
return Activator.CreateInstance(typeof(TClient), new object[] { endPointConfig, endPointAdress }) as TClient;
}
/// <summary>
/// Selects the best endpoint
/// </summary>
/// <param name="endpoints">The endpoint collection to choose from</param>
/// <returns>the endpoint of choice</returns>
private ServiceEndpoint ChooseBestEndPoint(ServiceEndpointCollection endpoints)
{
if (BindingID < endpoints.Count)
return endpoints[BindingID];
else
return endpoints[0];
}
/// <summary>
/// Tries to read the service WSDL either at /mex (POST) endpoint or at the ?WSDL (GET) url if it is http
/// </summary>
/// <param name="serviceURL">The URL of the Service</param>
/// <returns>the collection endpoints offered by the Service</returns>
private ServiceEndpointCollection DiscoverServiceEndpoints(string serviceURL)
{
MetadataSet mds = null;
WSHttpBinding binding = new System.ServiceModel.WSHttpBinding(System.ServiceModel.SecurityMode.None);
binding.MaxReceivedMessageSize = int.MaxValue;
MetadataExchangeClient mexClient = new System.ServiceModel.Description.MetadataExchangeClient(binding);
// First try to query the mex endpoint
try
{
mds = mexClient.GetMetadata(new System.ServiceModel.EndpointAddress(serviceURL + "/mex"));
}
catch (InvalidOperationException ex)
{
if ((ConfigurationManager.GetSection("loggingConfiguration") != null) && (Logger.IsLoggingEnabled()))
Logger.Write(ex, new List<string> { "General" }, 1, ERROR_MEX_FAILURE, System.Diagnostics.TraceEventType.Warning, "Connection to Mex Endpoint using WSHTTPBinding has failed");
}
// If not succesful try to query the ?WSDL adress if it is a http based service
if ((mds == null) && (new Uri(serviceURL).Scheme == "http"))
try
{
mds = mexClient.GetMetadata(new Uri(serviceURL + "?WSDL"),MetadataExchangeClientMode.HttpGet);
}
catch (InvalidOperationException ex)
{
if ((ConfigurationManager.GetSection("loggingConfiguration") != null) && (Logger.IsLoggingEnabled()))
Logger.Write(ex, new List<string> { "General" }, 1, ERROR_WSDL_FAILURE, System.Diagnostics.TraceEventType.Error, "Connection to ?WSDL URL using WSHTTPBinding has failed. MetadataExchangeClient failed");
throw new ConfigurationErrorsException("MetadataExchangeClient failed with: " + ex.Message, ex);
}
// Verify we got a ServiceDescription Section
if (mds.MetadataSections.Count(section => section.Metadata is System.Web.Services.Description.ServiceDescription) < 1)
{
if ((ConfigurationManager.GetSection("loggingConfiguration") != null) && (Logger.IsLoggingEnabled()))
Logger.Write("Metadataset contains no service description. Maybe it is not a service?", new List<string> { "General" }, 1, ERROR_MDS_FAILURE, System.Diagnostics.TraceEventType.Error, "Metadataset contains no service description. Maybe it is not a service?");
throw new ConfigurationErrorsException("Metadata does not contain a service description");
}
WsdlImporter importer = new WsdlImporter(mds);
return importer.ImportAllEndpoints();
}
#endregion
#region PublicMethods
/// <summary>
/// Creates a Proxy from Service URL and name of the Endpoint configuration in config file
/// </summary>
/// <typeparam name="ServiceType">The service client type</typeparam>
/// <typeparam name="ServiceInterface">Service Interface Type</typeparam>
/// <param name="serviceURL">The URL of the service</param>
/// <param name="endpointConfig">The name of the endpoint configuration in configuration file</param>
/// <returns>a new client</returns>
public TClient CreateProxyFromWSDL(string serviceURL,string endpointConfig)
{
return CreateProxyFromWSDL(serviceURL,null,endpointConfig);
}
/// <summary>
/// Creates a Duplex capable Proxy from Service URL and name of the Endpoint configuration in config file
/// </summary>
/// <typeparam name="ServiceType">The service client type</typeparam>
/// <typeparam name="ServiceInterface">Service Interface Type</typeparam>
/// <param name="serviceURL">The URL of the service</param>
/// <param name="CallbackInstance">The CallbackInstance for Duplex</param>
/// <param name="endpointConfig">The name of the endpoint configuration in configuration file</param>
/// <returns>a new client</returns>
public TClient CreateProxyFromWSDL(string serviceURL, object CallbackInstance,string endpointConfig)
{
ServiceEndpoint endpoint = ChooseBestEndPoint(DiscoverServiceEndpoints(serviceURL));
return ActivateProxy(CallbackInstance, endpointConfig,endpoint.Address.Uri.ToString());
}
/// <summary>
/// Creates a new Proxy from ServiceURL and custom configuration properties
/// </summary>
/// <typeparam name="ServiceType">The service client type</typeparam>
/// <typeparam name="ServiceInterface">Service Interface Type</typeparam>
/// <param name="serviceURL">The URL of the service</param>
/// <param name="properties">The configuration properties</param>
/// <returns>a new client</returns>
public TClient CreateProxyFromWSDL(string serviceURL,BindingConfigurationHelper.BindingProperties properties)
{
return CreateProxyFromWSDL(serviceURL,null,properties);
}
/// <summary>
/// Creates a new Proxy from ServiceURL
/// </summary>
/// <typeparam name="ServiceType">The service client type</typeparam>
/// <typeparam name="ServiceInterface">Service Interface Type</typeparam>
/// <param name="serviceURL">The URL of the service</param>
/// <returns>a new client</returns>
public TClient CreateProxyFromWSDL(string serviceURL)
{
return CreateProxyFromWSDL(serviceURL, null, null as BindingConfigurationHelper.BindingProperties);
}
/// <summary>
/// Creates a new duplex capable Proxy from ServiceURL
/// </summary>
/// <typeparam name="ServiceType">The service client type</typeparam>
/// <typeparam name="ServiceInterface">Service Interface Type</typeparam>
/// <param name="serviceURL">The URL of the service</param>
/// <param name="CallbackInstance">The CallbackInstance for Duplex</param>
/// <returns>a new client</returns>
public TClient CreateProxyFromWSDL(string serviceURL, object CallBackInstance)
{
return CreateProxyFromWSDL(serviceURL, CallBackInstance, null as BindingConfigurationHelper.BindingProperties);
}
/// <summary>
/// Creates a new duplex capable Proxy from ServiceURL. Applies Default parameters if "UseBindingDefaults" appsetting is set to "1" in config file
/// </summary>
/// <typeparam name="ServiceType">The service client type</typeparam>
/// <typeparam name="ServiceInterface">Service Interface Type</typeparam>
/// <param name="serviceURL">The URL of the service</param>
/// <param name="CallbackInstance">The CallbackInstance for Duplex</param>
/// <param name="properties">The custom configuration properties</param>
/// <returns>a new client</returns>
public TClient CreateProxyFromWSDL(string serviceURL,object CallbackInstance,BindingConfigurationHelper.BindingProperties properties)
{
ServiceEndpoint endpoint = ChooseBestEndPoint(DiscoverServiceEndpoints(serviceURL));
if (ConfigurationManager.AppSettings["UseBindingDefaults"] == "1")
{
if (properties == null)
properties = new BindingConfigurationHelper.BindingProperties();
properties.Add("MaxReceivedMessageSize", int.MaxValue);
properties.Add("MaxBufferSize", int.MaxValue);
properties.Add("MaxItemsInObjectGraph", int.MaxValue);
}
if (properties != null)
{
BindingConfigurationHelper myHelper = new BindingConfigurationHelper();
myHelper.ConfigureClientBinding(endpoint.Binding,properties);
}
TClient ret_obj = ActivateProxy(CallbackInstance,endpoint);
if ((properties!=null) && (properties.ContainsKey("MaxItemsInObjectGraph") && ret_obj != null))
if ((ret_obj as ClientBase<TServiceInterface>).Endpoint.Behaviors.Find<CustomDataContractSerializerOperationBehavior>() == null)
(ret_obj as ClientBase<TServiceInterface>).ChannelFactory.Endpoint.Behaviors.Add(new CustomDataContractSerializerOperationBehavior() { MaxItemsInObjectGraph = Convert.ToInt32(properties["MaxItemsInObjectGraph"]) });
return ret_obj;
}
#endregion
}
}