WCF服务的使用反射动态调用 [英] Dynamic Invocation of WCF Service Using Reflection

查看:888
本文介绍了WCF服务的使用反射动态调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个问题,传递一个泛型集合到WCF服务方法使用反射时调用。具体来说,集合类型的名单,其中,KeyValuePair<字符串,字符串>>

I'm having a problem passing a generic collection to a WCF service method when invoked using reflection. Specifically, the collection is of type List<KeyValuePair<string,string>>.

我的目标是能够在不添加任何在我的客户端应用程序与服务的任何引用在动态运行时执行WCF服务的方法。用户应该能够在运行时添加服务和应用程序应该只是奇迹般地能够处理它。

My goal is to be able to execute methods of a WCF service dynamically during runtime without adding any references to the service whatsoever in my client app. A user should be able to add a service during runtime and the app should just magically be able to handle it.

服务接口

[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!";
    }
}

客户端code

        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);
        }

引发的错误是:

The error that is thrown is:

{对象   'System.Collections.Generic.List<$c$c>1[System.Collections.Generic.KeyValuePair2[System.String,System.String]]'   不能被转换为类型   System.Collections.Generic.KeyValuePairOfstringstring []'。}

{"Object of type 'System.Collections.Generic.List1[System.Collections.Generic.KeyValuePair2[System.String,System.String]]' cannot be converted to type 'System.Collections.Generic.KeyValuePairOfstringstring[]'."}

请记住,这个光荣的工作对 ProcessSimpleType(...)方法测试,并传递一个简单类型时。我的问题是只能用 ProcessGenericCollection(...)。有没有人遇到过这个问题,如果是这样,你是怎么克服的?

Keep in mind, this works gloriously when testing against the ProcessSimpleType(...) method and passing in a simple type. My issue is only with ProcessGenericCollection(...). Has anyone ever encountered this issue, and if so, how did you overcome it?

推荐答案

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

Thanks to a colleague for providing the solution. For those of you with a similar issue, I inserted the following:

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

这篇关于WCF服务的使用反射动态调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆