用反射动态调用WCF服务

时间:2014-09-12 00:05:44

标签: c# .net wcf reflection

我在使用反射调用时将泛型集合传递给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(...)。有没有人遇到过这个问题,如果有的话,你是怎么克服它的?

1 个答案:

答案 0 :(得分:1)

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

...
...
        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();
...
...