2014-09-12 4 views
4

У меня возникла проблема с передачей обобщенной коллекции в метод службы WCF при вызове с использованием отражения. В частности, коллекция имеет тип List<KeyValuePair<string,string>>.Динамический вызов службы WCF с использованием отражения

Моя цель - динамически выполнять методы службы WCF во время выполнения без добавления каких-либо ссылок на эту услугу в моем клиентском приложении. Пользователь должен иметь возможность добавлять службу во время выполнения, и приложение должно просто волшебным образом справиться с этим.

Service Interface

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

Внедрение Service

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

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

Ошибка, которая выбрасывается в:

{ "Объект типа 'System.Collections.Generic.List 1[System.Collections.Generic.KeyValuePair 2 [System.String, System.String]]' не может быть преобразовано в тип «System .Collections.Generic.KeyValuePairOfstringstring []»."}

Имейте в виду, что это работает великолепно при тестировании против метода ProcessSimpleType(...) и переходящая в простом виде. Моя проблема только с ProcessGenericCollection(...). Кто-нибудь когда-либо сталкивался с этой проблемой, и если да, то как вы ее преодолели?

ответ

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(); 
... 
... 
Смежные вопросы