5

プログラムで 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;
        }
4

3 に答える 3

5

ご存知のように、svcutil はこのオプション (/reference フラグ) をサポートしています。必要なのは、リフレクターで 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);
            }
        }
    }
}
于 2012-05-22T17:56:14.207 に答える
1

これが、プロパティServiceContractGenerator.ReferencedTypesの目的です。コントラクトに対応するマッピング タイプを追加するだけです。

于 2012-05-24T05:05:48.920 に答える
1

タイプを再利用したいアセンブリがWebサービスのコントラクトで実際に使用されている場合、@peerが提案したようにそれを追加するだけでおそらくうまくいくはずです! svcutil が必要とするのは、彼が言ったことに対応する /reference オプションだけです。そうでない場合、svcutil.exe は、アセンブリの型 "A" とサービスの型 "A" が同じではないと見なします。名前、名前空間、または XML 名前空間に微妙な違いがあります (またはスキーマが実際には異なります)。

サービス参照を追加するときに「既存の型を再利用する」が無視されることを確認してください。つまり、「既存のアセンブリ」内の「既存の型」が実際に同じスキーマ領域にマップされていることを確認してください。その契約属性は、型を定義するアセンブリ内にある必要があることに注意してください! それがあなたのものであれば、追加して再コンパイルするだけです。

また、コンソールで svcutil を手動で実行してみましたか? そこに追加のエラー/警告が表示される可能性があり、実際の問題が何であるかを指摘する可能性があります。

于 2012-05-22T14:02:40.420 に答える