当使用反射调用时,我在将泛型集合传递给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(...)
。有人遇到过这个问题吗?如果有,你是如何克服的?
感谢同事提供的解决方案。对于那些有类似问题的人,我插入了以下内容:
...
...
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();
...
...