使用反射动态调用WCF服务

本文关键字:WCF 服务 调用 动态 反射 | 更新日期: 2023-09-27 18:21:45

当使用反射调用时,我在将泛型集合传递给WCF服务方法时遇到问题。具体而言,该集合的类型为List<KeyValuePair<string,string>>.

我的目标是能够在运行时动态执行WCF服务的方法,而无需在我的客户端应用程序中添加任何对该服务的引用。用户应该能够在运行时添加服务,应用程序应该能够神奇地处理它

服务接口

[ServiceContract]    
public interface ITestService
{
    [OperationContract]
    string ProcessSimpleType(string value);
    [OperationContract]
    string ProcessGenericCollection(List<KeyValuePair<string, string>> genericCol);
}

服务实施

public class TestService : ITestService
{
    public string ProcessSimpleType(string value)
    {
        return value;
    }
    public string ProcessGenericCollection(List<KeyValuePair<string, string>> genericCol)
    {
        return "Hello World!";
    }
}

客户端代码

        try
        {
            Uri mexAddress = new Uri("http://localhost:8732/TestService/?wsdl");
            MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;
            string contractName = "ITestService";
            string operationName = "ProcessGenericCollection";
            List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
            list.Add(new KeyValuePair<string, string>("key", "value"));

            object[] operationParameters = new object[] { list };
            MetadataExchangeClient mexClient = new MetadataExchangeClient(mexAddress, mexMode);
            mexClient.ResolveMetadataReferences = true;
            MetadataSet metaSet = mexClient.GetMetadata();
            WsdlImporter importer = new WsdlImporter(metaSet);
            Collection<ContractDescription> contracts = importer.ImportAllContracts();
            ServiceEndpointCollection allEndpoints = importer.ImportAllEndpoints();
            ServiceContractGenerator generator = new ServiceContractGenerator();
            var endpointsForContracts = new Dictionary<string, IEnumerable<ServiceEndpoint>>();
            foreach (ContractDescription contract in contracts)
            {
                generator.GenerateServiceContractType(contract);
                endpointsForContracts[contract.Name] = allEndpoints.Where(
                    se => se.Contract.Name == contract.Name).ToList();
            }
            if (generator.Errors.Count != 0)
                throw new Exception("There were errors during code compilation.");
            CodeGeneratorOptions options = new CodeGeneratorOptions();
            options.BracingStyle = "C";
            CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");
            CompilerParameters compilerParameters = new CompilerParameters(
                new string[] { 
                "System.dll", "System.ServiceModel.dll", 
                "System.Runtime.Serialization.dll" });
            compilerParameters.GenerateInMemory = true;
            CompilerResults results = codeDomProvider.CompileAssemblyFromDom(
                compilerParameters, generator.TargetCompileUnit);
            if (results.Errors.Count > 0)
            {
                throw new Exception("There were errors during generated code compilation");
            }
            else
            {
                Type clientProxyType = results.CompiledAssembly.GetTypes().First(
                    t => t.IsClass &&
                        t.GetInterface(contractName) != null &&
                        t.GetInterface(typeof(ICommunicationObject).Name) != null);
                ServiceEndpoint se = endpointsForContracts[contractName].First();
                object instance = results.CompiledAssembly.CreateInstance(
                    clientProxyType.Name,
                    false,
                    System.Reflection.BindingFlags.CreateInstance,
                    null,
                    new object[] { se.Binding, se.Address },
                    CultureInfo.CurrentCulture, null);

                var methodInfo = instance.GetType().GetMethod(operationName);
                //Invoking the ProcessGenericCollection via reflection will throw an exception
                object retVal = methodInfo.Invoke(instance, BindingFlags.InvokeMethod, null, operationParameters, null);

                Console.WriteLine(retVal.ToString());
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

抛出的错误是:

{"类型的对象'System.Collections.Generic.List 1[System.Collections.Generic.KeyValuePair 2[System.String,System.String]]'无法转换为类型'System.Collections.Generic.KeyValuePairOfstringstring[]'。"}

请记住,当针对ProcessSimpleType(...)方法进行测试并传入一个简单类型时,这种方法非常有效。我的问题仅限于ProcessGenericCollection(...)。有人遇到过这个问题吗?如果有,你是如何克服的?

使用反射动态调用WCF服务

感谢同事提供的解决方案。对于那些有类似问题的人,我插入了以下内容:

...
...
        WsdlImporter importer = new WsdlImporter(metaSet);
        //BEGIN INSERT
        XsdDataContractImporter xsd = new XsdDataContractImporter();
        xsd.Options = new ImportOptions();
        xsd.Options.ImportXmlType = true;
        xsd.Options.GenerateSerializable = true;
        xsd.Options.ReferencedTypes.Add(typeof(KeyValuePair<string, string>));
        xsd.Options.ReferencedTypes.Add(typeof(System.Collections.Generic.List<KeyValuePair<string, string>>));
        importer.State.Add(typeof(XsdDataContractImporter), xsd);
        //END INSERT
        Collection<ContractDescription> contracts = importer.ImportAllContracts();
...
...