WCF service proxy on the fly dynamically using WSDL url

0 votes
asked Jul 3, 2014 in WCF by warne

I am trying to implement a generic code for creating WCF service proxy on the fly dynamically from WSDL Url. My method just should accept parameters of WCF service WSDL url, the Operation name and the pay load array of the operation. I do not want to have my parameters and return types as string or any generic types. Developers should not change anything in the way they create the services. That means, the parameters and return types must be strongly typed any number of arguments etc. My client application should not need to add any service reference to call service operation. All they need to do is, call the generic method which I am going to write and pass WSDL url, method name, params array. Is it possible to do?

Share

1 Answer

+1 vote
answered Jul 4, 2014 by Aadhira
selected Jul 4, 2014 by administrator
 
Best answer

Yes absolutely possible. Below example gives you the baseline for your requirement. In fact, the similar process is what happening when you do add service reference using visual studio. But only difference is, when you add service reference, the assembly fill will be created physically. But here, we are keeping everything in memory. In this example, we will download the WSDL from the WSDL url using a .Net helper class “WsdlImporter”. From that, the code finds all the Contracts and the endpoints available in the WSDL description. Then we create c# assembly with classes for contracts on the fly dynamically (Service Contract and Data Contract). Once we have the assembly, we will create proxy for the service and call the operation. This code may not be working 100% for all scenarios. This is not production ready. I have done anything related to security etc. But as I said, it would be a starting point for you.

public class ServiceDetail
    {
        public Uri WSDLUri { get; set; }
        public Uri ServiceUri { get; set; }
        public String ContractName { get; set; }
        public string MethodName { get; set; }
    }

    public class GenericService
    {
        public void Call(ServiceDetail svc, List<object> payLoads)
        {
            //Import WSDL
            WsdlImporter imptr = ImportWSDL(svc.WSDLUri);
            //Extract Service and Data Contract Descriptions
            Collection<ContractDescription> svcCtrDesc = imptr.ImportAllContracts();
            //Compile the description to assembly
            var assembly = GetAssembly(svcCtrDesc);
            if (assembly == null) return;
            //Extract all end points available on the WSDL
            IDictionary<string, IEnumerable<ServiceEndpoint>> allEP = GetEndPointsOfEachServiceContract(imptr, svcCtrDesc);
            IEnumerable<ServiceEndpoint> currentSvcEP;
            if (allEP.TryGetValue(svc.ContractName, out currentSvcEP))
            {
                //Find the endpoint of the service to which the proxy needs to contact
                var svcEP = currentSvcEP.First(x => x.ListenUri.AbsoluteUri == svc.ServiceUri.AbsoluteUri);
                //Generate proxy
                var proxy = GetProxy(svc.ContractName, svcEP, assembly);
                //Deserialize each payload argument to object
                List<object> pls = new List<object>();
                foreach (var pl in payLoads)
                {
                    object clrObj = null;
                    try
                    {
                        clrObj = Deserialize(pl.ToString(), assembly);
                    }
                    catch
                    {
                        clrObj = pl;
                    }
                    pls.Add(clrObj);
                }
                //Find opration contract on the proxy and invoke
                proxy.GetType().GetMethod(svc.MethodName).Invoke(proxy, pls.ToArray());
            }
            return;
        }

        private Assembly GetAssembly(Collection<ContractDescription> svcCtrDesc)
        {
            CodeCompileUnit ccu = GetServiceAndDataContractCompileUnitFromWSDL(svcCtrDesc);
            CompilerResults rslt = GenerateContractsAssemblyInMemory(new CodeCompileUnit[] { ccu });
            if (!rslt.Errors.HasErrors)
                return rslt.CompiledAssembly;
            return null;
        }
        private object GetProxy(string ctrName, ServiceEndpoint svcEP, Assembly assembly)
        {
            Type prxyT = assembly.GetTypes().First(t => t.IsClass && t.GetInterface(ctrName) != null && t.GetInterface(typeof(ICommunicationObject).Name) != null);
            object proxy = assembly.CreateInstance(prxyT.Name,false,System.Reflection.BindingFlags.CreateInstance,
                                    null,new object[] { svcEP.Binding, svcEP.Address },CultureInfo.CurrentCulture, null);
            return proxy;
        }
        private WsdlImporter ImportWSDL(Uri wsdlLoc)
        {
            MetadataExchangeClient mexC = new MetadataExchangeClient(wsdlLoc, MetadataExchangeClientMode.HttpGet);
            mexC.ResolveMetadataReferences = true;
            MetadataSet metaSet = mexC.GetMetadata();
            return new WsdlImporter(metaSet);
        }
        private Dictionary<string, IEnumerable<ServiceEndpoint>> GetEndPointsOfEachServiceContract(WsdlImporter imptr, Collection<ContractDescription> svcCtrDescs)
        {
            ServiceEndpointCollection allEP = imptr.ImportAllEndpoints();
            var ctrEP = new Dictionary<string, IEnumerable<ServiceEndpoint>>();
            foreach (ContractDescription svcCtrDesc in svcCtrDescs)
            {
                List<ServiceEndpoint> eps = allEP.Where(x => x.Contract.Name == svcCtrDesc.Name).ToList();
                ctrEP.Add(svcCtrDesc.Name, eps);
            }
            return ctrEP;
        }
        private CodeCompileUnit GetServiceAndDataContractCompileUnitFromWSDL(Collection<ContractDescription> svcCtrDescs)
        {
            ServiceContractGenerator svcCtrGen = new ServiceContractGenerator();
            foreach (ContractDescription ctrDesc in svcCtrDescs)
            {
                svcCtrGen.GenerateServiceContractType(ctrDesc);
            }
            return svcCtrGen.TargetCompileUnit;
        }
        private object Deserialize(string xml, Assembly assembly)
        {
            Type ctr = GetDataContractType(xml, assembly);
            return Deserialize(xml, ctr);
        }
        private object Deserialize(string xml, Type toType)
        {
            using (Stream stream = new MemoryStream())
            {
                byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
                stream.Write(data, 0, data.Length);
                stream.Position = 0;
                DataContractSerializer d = new DataContractSerializer(toType);
                return d.ReadObject(stream);
            }
        }
        private Type GetDataContractType(string xml, Assembly assembly)
        {
            var serializedXML = ConvertToXML(xml);
            var match = assembly.GetTypes().First(x => x.Name == serializedXML.Root.Name.LocalName);
            return match;
        }
        private XDocument ConvertToXML(string xml)
        {
            using (Stream stream = new MemoryStream())
            {
                byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
                stream.Write(data, 0, data.Length);
                stream.Position = 0;
                return XDocument.Load(stream);
            }
        }
        private CompilerResults GenerateContractsAssemblyInMemory(params CodeCompileUnit[] codeCompileUnits)
        {
            // Generate a code file for the contracts 
            CodeGeneratorOptions opts = new CodeGeneratorOptions();
            opts.BracingStyle = "C";
            CodeDomProvider pro = CodeDomProvider.CreateProvider("C#");
            // Compile the code file to an in-memory assembly
            // Don't forget to add all WCF-related assemblies as references
            CompilerParameters prms = new CompilerParameters(new string[] { "System.dll", "System.ServiceModel.dll", 
                "System.Runtime.Serialization.dll"});
            prms.GenerateInMemory = true;
            prms.GenerateExecutable = false;
            return pro.CompileAssemblyFromDom(prms, codeCompileUnits);
        }
    }
commented Dec 21, 2014 by Yaswanth
Hi,

I am getting exception
var res= proxy.GetType().GetMethod(svc.MethodName).Invoke(proxy, pls.ToArray());

eventhough i m passing the correct object as parameter.Could you please help me in that
commented Dec 22, 2014 by administrator (315 points)
Please add the detail about your exception, so that any one can help
commented Dec 8, 2016 by anonymous
What is List<object> payLoads? what I need to pass as parameter?

I want to invoke a method that takes parameter XML as a string and returns acknowledgement. Is that possible?

Your answer

Preview

Your name to display (optional):
Privacy: Your email address will only be used for sending these notifications.
Anti-spam verification:
To avoid this verification in future, please log in or register.
site design / logo / content © 2013 - 2015 pinfaq.com
...