简体   繁体   中英

C# WCF Dynamically

My Req. is I want to call WCF Service Dynamically, just WSDL and interface name and method name will be provided, arguments given in dictionary/any collection. I want to call an operation contract dynamically which has parametrs as simple type and complex types.

My Service:-

public class CalculatorService : ICalculator
{
    public double Add(double n1, double n2)
    {
        return n1 + n2;
    }
    public EmployeeOutput ProcessEmployee(EmployeeInput obj,string str, int i)
    {
        EmployeeOutput objOut = new EmployeeOutput();
        objOut.EmpNo = obj.EmpNo;
        objOut.EmpName = obj.EmpName;
        return objOut;
    }
[DataContract]
    public class EmployeeInput
    {
    [DataMember]
    public string EmpNo { get; set; }

    [DataMember]
    public string EmpName { get; set; }
}

From client, I am trying to do this, since I had complex Type(EmployeeInput and primitive type string, int) and in complex type I had properties and fields, issue I am facing is mapping the inputs to service parameters for complex types as well as primitive types. My Code at client:

    static class ObjectExtensions
    {
        public static void SetPropertyValue<T>(this object obj, string propertyName, T propertyValue)
        where T : IConvertible
    {
        PropertyInfo pi = obj.GetType().GetProperty(propertyName);

        if (pi != null && pi.CanWrite)
        {
            pi.SetValue
            (
                obj,
                Convert.ChangeType(propertyValue, pi.PropertyType),
                null
            );
        }
    }
}
class Program
    {
        static void Main(string[] args)
        {
            CompilerResults compilerResults = null;

        object proxyInstance = GetProxyInstance(ref compilerResults);
        string operationName = "ProcessEmployee";
        MethodInfo methodInfo = proxyInstance.GetType().GetMethod(operationName);
        ParameterInfo[] paramInfos = methodInfo.GetParameters();
        OrderedDictionary valueList = new OrderedDictionary();
        IList<Object> parameter = new List<Object>();
              //Object[] parameter = new Object[10];
        for (int paramIndex = 0; paramIndex < paramInfos.Length; paramIndex++)
        {
            int i = 1;
            var parameterType = paramInfos[paramIndex].ParameterType;
            bool IsPrimitive = parameterType.GetType().IsPrimitive;
            Object parameter2 = new Object();
            if (parameterType.Namespace.StartsWith("System"))
            {
                parameter2.SetPropertyValue("str", "Test");
                //parameterType.GetProperties()[i].SetValue(parameter2, "Test", null);
            }
            else
            {
                parameter2 = compilerResults.CompiledAssembly.CreateInstance(parameterType.FullName, false, BindingFlags.CreateInstance, null, null, null, null);
                EmployeeInput obj = new EmployeeInput();
                obj.EmpNo = "123";
                obj.EmpName = "Test";
                Console.WriteLine(parameter2.GetType());
                Console.WriteLine(obj.GetType());
                parameter2.SetPropertyValue("EmpNo", 123);
                parameter2.SetPropertyValue("EmpName", "Test");
                //parameterType.GetProperties()[i].SetValue(parameter2, Convert.ChangeType(obj, parameter2.GetType()), null);
                //parameterType.GetProperties()[i].SetValue(parameter2, obj, null);
            }
            parameter.Add(parameter2);
            //parameter[paramIndex] = parameter2;
            i++;
        }
 object[] operationParameters = new object[] { parameter };
 var result = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null);

The above code is failing when mapping complex fields. I mean from client, client send a collection, ex:- Dictionary collection with complex types, primitive types, I should be able to invoke my operationContract at service Side, this is my requirement.

WSDL Code Generation I'm already doing in CreateInstance,

        public static object GetProxyInstance(ref CompilerResults compilerResults)
    {
        object proxyInstance = null;
        // Define the WSDL Get address, contract name and parameters, with this we can extract WSDL details any time
        //Uri address = new Uri("http://localhost:8732/CalculatorService/?wsdl");
        //Uri address = new Uri("http://localhost/APG.WCFService/APGService.svc?wsdl");
        Uri address = new Uri("http://localhost:8732/CalculatorService/?wsdl");
        // For HttpGet endpoints use a Service WSDL address a mexMode of .HttpGet and for MEX endpoints use a MEX address and a mexMode of .MetadataExchange
        MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;
        string contractName = "ICalculator";
        //string contractName = "IAPGService";
        // Get the metadata file from the service.
        MetadataExchangeClient metadataExchangeClient = new MetadataExchangeClient(address, mexMode);
        metadataExchangeClient.ResolveMetadataReferences = true;
        //One can also provide credentials if service needs that by the help following two lines.
        //ICredentials networkCredential = new NetworkCredential("", "", "");
        //metadataExchangeClient.HttpCredentials = networkCredential;
        //Gets the meta data information of the service.
        MetadataSet metadataSet = metadataExchangeClient.GetMetadata();
        // Import all contracts and endpoints.
        WsdlImporter wsdlImporter = new WsdlImporter(metadataSet);
        //Import all contracts.
        Collection<ContractDescription> contracts = wsdlImporter.ImportAllContracts();
        //Import all end points.
        ServiceEndpointCollection allEndpoints = wsdlImporter.ImportAllEndpoints();
        // Generate type information for each contract.
        ServiceContractGenerator serviceContractGenerator = new ServiceContractGenerator();
        //Dictinary has been defined to keep all the contract endpoints present, contract name is key of the dictionary item.
        var endpointsForContracts = new Dictionary<string, IEnumerable<ServiceEndpoint>>();
        foreach (ContractDescription contract in contracts)
        {
            serviceContractGenerator.GenerateServiceContractType(contract);
            // Keep a list of each contract's endpoints.
            endpointsForContracts[contract.Name] = allEndpoints.Where(ep => ep.Contract.Name == contract.Name).ToList();
        }
        // Generate a code file for the contracts.
        CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();
        codeGeneratorOptions.BracingStyle = "C";
        // Create Compiler instance of a specified language.
        CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");
        // Adding WCF-related assemblies references as copiler parameters, so as to do the compilation of particular service contract.
        CompilerParameters compilerParameters = new CompilerParameters(new string[] { "System.dll", "System.ServiceModel.dll", "System.Runtime.Serialization.dll" });
        compilerParameters.GenerateInMemory = true;
        //Gets the compiled assembly.
        compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters, serviceContractGenerator.TargetCompileUnit);
        if (compilerResults.Errors.Count <= 0)
        {
            // Find the proxy type that was generated for the specified contract (identified by a class that implements the contract and ICommunicationbject - this is contract 
            //implemented by all the communication oriented objects).
            Type proxyType = compilerResults.CompiledAssembly.GetTypes().First(t => t.IsClass && t.GetInterface(contractName) != null &&
                t.GetInterface(typeof(ICommunicationObject).Name) != null);
            // Now we get the first service endpoint for the particular contract.
            ServiceEndpoint serviceEndpoint = endpointsForContracts[contractName].First();
            // Create an instance of the proxy by passing the endpoint binding and address as parameters.
            proxyInstance = compilerResults.CompiledAssembly.CreateInstance(proxyType.Name, false, System.Reflection.BindingFlags.CreateInstance, null,
                new object[] { serviceEndpoint.Binding, serviceEndpoint.Address }, CultureInfo.CurrentCulture, null);
        }
        return proxyInstance;


    } 

Thanks In Advance,

I suggest you make use of the Wsdl utility tool to generate the web service proxy dynamically. Then using the CodeDom to compile the code to a runtime assembly and call it dynamically from your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM