I had a problem passing a generic collection to a WCF service method when called using reflection. In particular, the collection is of typeList<KeyValuePair<string,string>>.
My goal is to dynamically execute WCF service methods at runtime without adding any links to this service in my client application. The user should be able to add the service at runtime, and the application should just magically handle it.
Service interface
[ServiceContract]
public interface ITestService
{
[OperationContract]
string ProcessSimpleType(string value);
[OperationContract]
string ProcessGenericCollection(List<KeyValuePair<string, string>> genericCol);
}
Service implementation
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);
object retVal = methodInfo.Invoke(instance, BindingFlags.InvokeMethod, null, operationParameters, null);
Console.WriteLine(retVal.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
The resulting error:
{"An object of type 'System.Collections.Generic.List 1[System.Collections.Generic.KeyValuePair2 [System.String, System.String]]' cannot be converted to type 'System.Collections.Generic.KeyValuePairOfstringstring []'." }
, ProcessSimpleType(...) . ProcessGenericCollection(...). - - , , ?