获得了一个带有插件的应用程序,它以编程方式生成WCF客户端契约,然后将其连接到插件接口,但是我很难弄清楚如何让生成的契约重用插件dll中的类型。
是否有人知道如何设置ServiceContractGenerator以重用已定义程序集中的类型?
这是我用来生成合同代码atm:
public Assembly CreateProxy(String url)
{
MetadataExchangeClient mexClient = new MetadataExchangeClient(new Uri(url + "/mex"), MetadataExchangeClientMode.MetadataExchange);
mexClient.ResolveMetadataReferences = true;
MetadataSet metaDocs = mexClient.GetMetadata();
WsdlImporter importer = new WsdlImporter(metaDocs);
ServiceContractGenerator generator = new ServiceContractGenerator();
generator.NamespaceMappings.Add("*", "NameSpace123");
Collection<ContractDescription> contracts = importer.ImportAllContracts();
ServiceEndpointCollection endpoints = importer.ImportAllEndpoints();
foreach (ContractDescription contract in contracts)
generator.GenerateServiceContractType(contract);
if (generator.Errors.Count != 0)
throw new Exception("There were errors during code compilation.");
CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");
CompilerParameters parameters = new CompilerParameters();
parameters.CompilerOptions = string.Format(@" /lib:{0}", "\"C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\v3.0\"");
parameters.ReferencedAssemblies.Add("System.ServiceModel.dll");
parameters.ReferencedAssemblies.Add("System.Runtime.Serialization.dll");
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
parameters.IncludeDebugInformation = true;
parameters.OutputAssembly = "WCFGenerated.dll";
CodeCompileUnit codeUnit = generator.TargetCompileUnit;
CompilerResults results = codeDomProvider.CompileAssemblyFromDom(parameters, codeUnit);
foreach (CompilerError oops in results.Errors)
throw new Exception("Compilation Error Creating Assembly: " + oops.ErrorText);
//Must load it like this otherwise the assembly wont match the one used for the generated code below
return Assembly.LoadFile(Directory.GetCurrentDirectory() + "\\WCFGenerated.dll");
}
编辑:我从来没有让它工作得很好,但是我确实设法将exe加载为程序集并使用它来生成代理:
try
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture();
if (Console.OutputEncoding.CodePage != Encoding.UTF8.CodePage && Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.OEMCodePage)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
}
var assembly = Assembly.LoadFile(Path.Combine(info.TempDir, SVCUTIL_EXE));
var optionsType = assembly.GetType("Microsoft.Tools.ServiceModel.SvcUtil.Options");
var runtimeType = assembly.GetType("Microsoft.Tools.ServiceModel.SvcUtil.ToolRuntime");
//Options option = Options.ParseArguments(args);
var options = optionsType.InvokeMember("ParseArguments", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, null, new object[] { info.Args });
//ToolRuntime toolRuntime = new ToolRuntime(option);
ConstructorInfo c = runtimeType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { optionsType }, null);
var runtime = c.Invoke(new Object[] { options });
//var runtime = Activator.CreateInstance(runtimeType, , null, options);
//toolRuntime.Run();
var exitCode = (int)runtimeType.InvokeMember("Run", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, runtime, null);
if (exitCode != 0)
throw new Exception(String.Format("Failed to generate wcf contract code [Bad Result: {0}]", exitCode));
}
catch (Exception e)
{
if (e is TargetInvocationException)
e = e.InnerException;
info.E = e;
}
答案 0 :(得分:5)
因为你已经知道svcutil支持这个选项(/ reference flag)。所以你需要的是在反射器中打开svcutil.exe并执行与此方法相同的操作:Microsoft.Tools.ServiceModel.SvcUtil.ImportModule + InitializationHelper.InitReferencedContracts
internal static void InitReferencedContracts(Options options, WsdlImporter importer, ServiceContractGenerator contractGenerator)
{
foreach (Type type in options.ReferencedTypes)
{
if (type.IsDefined(typeof(ServiceContractAttribute), false))
{
try
{
ContractDescription contract = ContractDescription.GetContract(type);
XmlQualifiedName key = new XmlQualifiedName(contract.Name, contract.Namespace);
importer.KnownContracts.Add(key, contract);
contractGenerator.ReferencedTypes.Add(contract, type);
continue;
}
catch (Exception exception)
{
if (Tool.IsFatal(exception))
{
throw;
}
throw new ToolRuntimeException(SR.GetString("ErrUnableToLoadReferenceType", new object[] { type.AssemblyQualifiedName }), exception);
}
}
}
}
答案 1 :(得分:1)
如果要重用类型的程序集实际上是由webservice的契约使用的,那么只需添加它就像@peer建议的应该可行!所有的svcutil都应该是/ reference选项,是什么映射到他所说的。如果没有,则svcutil.exe认为从程序集中键入“A”并从服务中键入“A”不相同。名称或命名空间之间存在细微差别,或者 - 在XML命名空间中(或模式实际上是不同的)。
请检查"Reuse existing types" is ignored when adding a service reference - 我的意思是,确保'现有程序集'中的'现有类型'确实映射到同一个模式领域。请注意,contract-attribute必须位于定义类型的程序集内!如果它是你的,只需添加并重新编译。
另外,您是否尝试在控制台中手动运行svcutil?你可能会在那里得到一些额外的错误/警告,也许他们会指出实际问题是什么。
答案 2 :(得分:1)
这是属性ServiceContractGenerator.ReferencedTypes的用途。只需为合同添加相应的映射类型。