以编程方式生成 WCF 客户端协定时的重用类型

本文关键字:定时 类型 客户端 编程 方式生 WCF | 更新日期: 2023-09-27 18:34:28

>有一个带有插件的应用程序,该应用程序以编程方式生成WCF客户端协定,然后将其连接到插件接口,但是我正在努力弄清楚如何让生成的协定重用插件dll中的类型。

有没有人知道如何设置服务合同生成器以重用定义的程序集中的类型?

这是我用来生成合约代码 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;
        }

以编程方式生成 WCF 客户端协定时的重用类型

正如

你已经知道的那样,svcutil支持此选项(/reference flag(。 所以你所需要的只是在反射器中打开 svcutil.exe 并执行与此方法相同的操作: Microsoft.Tools.ServiceModel.SvcUtil.ImportModule+InitializationHelper.InitReferencedContract

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

如果您要重用类型的程序集实际上被 Web 服务的合约使用,那么只需按照建议@peer添加它应该就可以了! SVCUTIL 需要的只是/reference 选项,它映射到他所说的内容。 如果不是,则 svcutil.exe 认为程序集中的类型"A"和服务中的类型"A"是不一样的。要么名称或命名空间存在细微差异,要么 XML 命名空间存在细微差异(或架构实际上不同(。

请在添加服务引用时检查"重用现有类型"被忽略 - 我的意思是,确保"现有程序集"中的"现有类型"确实映射到相同的架构领域。请注意,该协定属性必须位于定义类型的程序集内!如果是你的,只需添加并重新编译。

另外,您是否尝试过在控制台中手动运行 SVCUTIL? 您可能会在那里收到一些额外的错误/警告,也许他们会指出实际问题是什么。

这就是 ServiceContractGenerator.ReferencedType 属性的用途。只需为合约添加相应的映射类型即可。