BizTalk公開WSDLで.NETプロキシに重複メソッドが生成される原因と解決策――MessageContractとsvcutilで衝突を回避する実践ガイド

BizTalk で公開した WSDL を .NET の「サービス参照の追加」から取り込むと、生成されたプロキシ クラスに同名のメソッドが二重定義されてビルド エラーになる――この現場あるあるを、根本原因から回避策、実装例、検証のコツまでまとめて体系的に解決します。ポイントは「WSDL/スキーマの一意性」と「svcutil の適切なオプション」です。

目次

事象の概要(再現手順と症状)

次のような手順で発生します。

  • BizTalk Server(例:BizTalk 2020)の WCF Publishing Wizard で XSD スキーマをサービスとして公開する。
  • .NET 側(.NET Framework/.NET 6+ いずれも)で 「サービス参照の追加」 または 「WCF Web Service Reference」 を使い、WSDL を読み込む。
  • 自動生成された Service.csReference.cs)内で、リクエスト/レスポンス クラスに対応するメソッドが重複し、コンパイル エラー(type already defines a member called … with the same parameter types など)が発生する。

典型的には、異なるポート(あるいは異なるスキーマ)に 同名のルート要素(例:SubmitRequestSubmitResponse)が存在し、WSDL の <wsdl:operation> 名も同名になるケースで再現します。

なぜ重複メソッドが生成されるのか(根本原因)

BizTalk の公開サービスでは、設計次第で次の対応が成りやすく、これが .NET 生成時の衝突を生みます。

  • ルート要素名 = メッセージ名 = Operation 名 になりやすい。
  • 別スキーマ/別ポートでも、同一のルート要素名(例:RequestResponse)を使うと、<wsdl:portType> が分かれていても 同名 Operation が出来上がる。
  • .NET の「サービス参照」生成は、Operation 名をメソッド名とみなして 1 つのクライアント型に寄せるため、同名メソッドが二重化しやすい。

さらに、既定の Document/Literal Wrapped スタイルではラッパ要素が同名になりがちで、XmlSerializer ベースの生成だと MessageContract が暗黙に合成され、署名が一致して コンパイル不可に陥ります。

解決の全体像(最短ルート)

まずは次の方針を決めると遠回りを防げます。

対応箇所具体策補足説明
BizTalk 側1. WSDL の確認
管理コンソールから公開エンドポイントの WSDL を取得し、<wsdl:operation> 名の重複を点検。
2. スキーマ/ポートの命名規則を見直す
同名ルート要素が複数スキーマにある場合は、必ずプレフィックスやドメイン名を付けて区別(例:Order_SubmitRequest / Invoice_SubmitRequest)。
BizTalk は ルート要素名=メッセージ名=Operation 名 へ連鎖しがち。ここで一意にできないと、以降の工程はすべて苦しくなる。
.NET 側1. svcutil.exe + /messageContract で生成
svcutil.exe /messageContract /serializer:DataContract <WSDL URL>
明示的な MessageContract 生成で、メソッド名を要素名から切り離し、重複を排除。
2. 名前空間の再割り当て
/namespace:*,Contoso.BizTalk.Contracts のように衝突しない名前空間へ集約。
「サービス参照の追加」は手軽だが、同名ラッパ要素で衝突しやすい。svcutil なら生成を オプションで精密制御できる。
共通WSDL を変更できない場合は、生成後に partial クラスでラッパを提供して衝突メソッドを隠蔽する。保守コストが増えるため、推奨は WSDL/スキーマ段階での一意化svcutil の活用。

BizTalk 側での具体的対処(WSDL/スキーマ設計)

Operation 名の重複チェック

公開済みエンドポイントの WSDL を取得し、次を確認します。

  • <wsdl:portType> 配下の <wsdl:operation name="...">グローバルで一意か。
  • soapAction(Operation のアクション URI)が 一意か。

もし Submit のような汎名が複数あるなら、ドメインを冠した Order_Submit / Invoice_Submit などに 明示改名しましょう。

スキーマの命名規則(実運用パターン)

  • ルート要素名は <業務領域>_<動詞><Request|Response> に統一(例:Order_CreateRequest)。
  • targetNamespace にも業務領域(例:http://schemas.contoso.com/order/2025/)を付与。
  • 同名ルート要素を流用しない。再利用は 単位(complexType)で行う。

SOAP Action(Action Mapping)の一意化

WCF Publishing Wizard では、Operation ごとに SOAP Action が自動生成されますが、明示の一意性を保つため命名方針を決めておきます(例:http://schemas.contoso.com/order/Submit)。.NET 側の [OperationContract(Action="...")] と対応します。

WSDL の良い/悪い例(抜粋)

<!-- 悪い例:異なる portType に同名 Operation -->
<wsdl:portType name="OrderPortType">
  <wsdl:operation name="Submit">...</wsdl:operation>
</wsdl:portType>
<wsdl:portType name="InvoicePortType">
  <wsdl:operation name="Submit">...</wsdl:operation>
</wsdl:portType>



...


...
 

.NET 側での具体的対処(svcutil 生成)

「サービス参照の追加」ではなく、svcutil をコマンドで実行すると衝突を回避しやすくなります。特に /messageContract は必須級です。

最小限の実用コマンド

# PowerShell 例:パスは環境に合わせて調整
$svcutil = "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\SvcUtil.exe"
$wsdl    = "&lt;WSDL の URL&gt;"
&amp; $svcutil `
  /messageContract `
  /serializer:DataContract `
  /namespace:*,Contoso.BizTalk.Contracts `
  /mergeConfig `
  /out:GeneratedProxy.cs `
  $wsdl
  • /messageContract:明示的に MessageContract を生成し、メソッド名と要素名の結び付きを緩める
  • /serializer:DataContractDataContractSerializer を使用。XmlSerializer より衝突が起きにくい。
  • /namespace:生成型を衝突のない名前空間へまとめる。
  • /mergeConfig:バインド/エンドポイント等を app.config にマージ。

生成物の取り込みと利用

  1. GeneratedProxy.cs をプロジェクトに追加。
  2. 同時に出力される output.config(または標準出力)から、system.serviceModel セクションを app.config/appsettings.json(変換のうえ)へ取り込む。
  3. クライアント コードから通常通り呼び出す。

呼び出しコード例(.NET Framework / ClientBase<T>)

using (var client = new Contoso.BizTalk.Contracts.OrderPortTypeClient())
{
    var req = new Order_SubmitRequestMessage
    {
        Body = new Order_SubmitRequest
        {
            OrderId = "A-001",
            Amount  = 1200M
        }
    };
    var res = client.Order_Submit(req);
    Console.WriteLine(res.Body.Status);
}

呼び出しコード例(.NET 6+ / ChannelFactory)

var factory = new ChannelFactory&lt;IOrderPortType&gt;("OrderPortTypeEndpoint");
var channel = factory.CreateChannel();
var request = new Order_SubmitRequestMessage
{
    Body = new Order_SubmitRequest { OrderId = "A-001", Amount = 1200M }
};
var response = channel.Order_Submit(request);
((IClientChannel)channel).Close();
factory.Close();

.NET 6+ の場合(dotnet-svcutil / Connected Services)

.NET 6+ または SDK スタイル プロジェクトでは、Visual Studio の WCF Web Service Reference または CLI の dotnet-svcutil が利用できます。オプション思想は svcutil と同じです。

dotnet-svcutil &lt;WSDL の URL&gt; \
  --messageContract \
  --serializer DataContract \
  --namespace "*,Contoso.BizTalk.Contracts" \
  -nologo

GUI から追加する場合も、生成オプションで MessageContract の明示化DataContractSerializer を選べる構成にします。

名前空間・型名の整理で衝突を避ける

同一ソリューション内に複数のサービス参照が並ぶ場合、/namespace は必須です。推奨は「契約(Contracts)」「クライアント(Clients)」を分ける構成です。

/namespace:*,Contoso.BizTalk.Contracts
/namespace:http://schemas.contoso.com/order/2025/,Contoso.BizTalk.Contracts.Order
/namespace:http://schemas.contoso.com/invoice/2025/,Contoso.BizTalk.Contracts.Invoice

これにより、型名の偶発的な一致や using の衝突を抑止できます。

WSDL を触れないときの現実解(ワークアラウンド)

partial クラスでのラッパ提供

自動生成コードを直接編集せず、別ファイルに partial を定義して、衝突するメソッドを包み直す方法です。再生成で上書きされません。

namespace Contoso.BizTalk.Contracts
{
    public partial class OrderPortTypeClient
    {
        public Order_SubmitResponse Submit(Order_SubmitRequest payload)
        {
            var msg = new Order_SubmitRequestMessage { Body = payload };
            var res = this.Order_Submit(msg);
            return res.Body;
        }
    }
}

ただしこの方法は 根本原因を残すため、長期的には BizTalk 側の一意化+svcutil 再生成へ移行してください。

ChannelFactory + 手動 Message(最終手段)

どうしても署名衝突が解けない場合、WSDL の Action とシリアル化形を理解したうえで System.ServiceModel.Channels.Message を手組みします。保守性が低いため、恒久策には非推奨です。

実践結果(ケーススタディ)

質問者のケースでは、WSDL は変更不要の制約があったため、.NET 側で svcutil /messageContract を用いてプロキシを再生成しました。その結果、重複メソッドは解消し、正しくリクエストを組み立てて送信できるようになりました。生成後は partial による薄いラッパを加え、アプリ側からの呼び出しを簡素化しています。

検証チェックリスト(導入前後の確認)

  • WSDL の operation 名がすべて一意である。
  • SOAP Action が一意で、[OperationContract(Action="...")] と一致する。
  • svcutil の生成オプションに /messageContract/serializer:DataContract を設定した。
  • /namespace で契約の名前空間を整理した。
  • 自動生成コードは直接編集せず、拡張は partial で行っている。
  • 本番 URL/証明書/バインド設定が app.config に反映されている。

トラブルシュート Q&A

Q. それでもメソッドが重複します。

A. WSDL 上で 別の portType に同名 operation が残っている可能性が高いです。BizTalk 側での命名見直しが最優先です。どうしても触れない場合は、svcutil に加えて、サービスを分割(エンドポイントを分ける)するのも手です。

Q. XmlSerializer のままで回避できますか?

A. 困難です。XmlSerializer はラッパ要素名に強く依存し、重複に弱い設計です。DataContractSerializer + MessageContract を推奨します。

Q. Visual Studio の GUI でなんとかできませんか?

A. WCF Web Service Reference(Connected Services)で生成オプションを調整できますが、細かな制御は CLI の svcutildotnet-svcutil が確実です。

Q. 再生成のたびに設定が消えます。

A. 生成ディレクトリに スクリプト化した svcutil コマンド(PowerShell/BAT)を保存し、CI から再生成可能にします。アプリ固有の拡張は partial に分離しておけば安全です。

Q. 「Cannot import wsdl:portType」などのインポート エラーが出る。

A. WSDL 内部の参照順/名前空間のゆらぎで起こることがあります。/serializer:DataContract/dataContractOnly の併用や、/r で既知の型を参照させると安定します。

よくある落とし穴とアンチパターン

  • 自動生成コード(Reference.cs)を直接編集する:再生成で消えます。拡張は常に partial で。
  • Operation 名に汎用語SubmitProcess)を使う:将来必ず衝突します。
  • 名前空間を省略して global namespace へ置く:長期的に地獄を見ます。契約は Contracts 命名空間に隔離しましょう。
  • XmlSerializer 固定:BizTalk のスキーマ構成と相性が悪いケースが多いです。

サンプル:MessageContract と OperationContract

[ServiceContract(Namespace = "http://schemas.contoso.com/order/2025/")]
public interface IOrderPortType
{
    [OperationContract(Action = "http://schemas.contoso.com/order/Order_Submit")]
    Order_SubmitResponseMessage Order_Submit(Order_SubmitRequestMessage request);
}

[MessageContract(IsWrapped = true, WrapperName = "Order_SubmitRequest", WrapperNamespace = "[http://schemas.contoso.com/order/2025/](http://schemas.contoso.com/order/2025/)")]
public class Order_SubmitRequestMessage
{
[MessageBodyMember]
public Order_SubmitRequest Body { get; set; }
}

[MessageContract(IsWrapped = true, WrapperName = "Order_SubmitResponse", WrapperNamespace = "[http://schemas.contoso.com/order/2025/](http://schemas.contoso.com/order/2025/)")]
public class Order_SubmitResponseMessage
{
[MessageBodyMember]
public Order_SubmitResponse Body { get; set; }
}

[DataContract(Namespace = "[http://schemas.contoso.com/order/2025/](http://schemas.contoso.com/order/2025/)")]
public class Order_SubmitRequest
{
[DataMember(Order = 1)] public string OrderId { get; set; }
[DataMember(Order = 2)] public decimal Amount  { get; set; }
}

[DataContract(Namespace = "[http://schemas.contoso.com/order/2025/](http://schemas.contoso.com/order/2025/)")]
public class Order_SubmitResponse
{
[DataMember(Order = 1)] public string Status { get; set; }
} 

IsWrappedWrapperName を明示することで、WSDL 側のラッパ名と一致させつつ、クライアント側のメソッド名は一意に保てる設計になります。

付録:自動化スクリプト(PowerShell)

param(
  [Parameter(Mandatory=$true)] [string] $WsdlUrl,
  [string] $OutDir = ".\Generated"
)

$svcutil = "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\SvcUtil.exe"
if (-not (Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null }

& $svcutil `  /messageContract`
/serializer:DataContract `  /namespace:*,Contoso.BizTalk.Contracts`
/mergeConfig `  /out:"$OutDir\GeneratedProxy.cs"`
/nologo `
$WsdlUrl

Write-Host "Generated: $OutDir\GeneratedProxy.cs" 

CI に組み込めば、WSDL 側の変更があっても 同一手順で安全に再生成できます。

用語ミニ解説

MessageContract WCF のメッセージ構造を明示する属性モデル。ラッパ要素やヘッダーを制御でき、生成時の衝突回避に効果的。 DataContractSerializer WCF 既定のシリアライザー。型定義に基づく直交的なシリアライズで、XmlSerializer より衝突に強い。 Document/Literal Wrapped SOAP メッセージでラッパ要素を用いる一般的なスタイル。ラッパ名が衝突原因になることがある。

まとめ(実務手順の指針)

  • BizTalk 側で「ルート要素名=Operation 名」の一意性を担保する(命名規則の徹底)。
  • .NET 側svcutil /messageContract /serializer:DataContract でプロキシ生成し、/namespace で衝突を抑止。
  • WSDL を変更できない場合は partial で薄いラッパ、もしくはエンドポイント分割で実務を回す。

この 3 点を組み合わせれば、「プロキシに重複メソッドが生成される」問題は安定して解消できます。生成のたびに壊れない仕組み(命名規則+スクリプト再生成)まで含めてテナント化しておくと、保守コストを最小化できます。

補足情報(Visual Studio 2022 以降の注意)

  • 同現象は VS 2022 でも報告されており、ツールのバグというより WSDL/スキーマ設計の問題であることが多いです。
  • WCF Web Service Reference(Connected Services)が導入されていても、MessageContract を明示しなければ衝突は再現します。
  • 既存システムでスキーマ名を変更できない場合、portType を分けて Operation 名を変える(アクションも分ける)ことでオーバーロードを避けられます。

ケース別対応表(早見)

ケース推奨アクション効果
同名ルート要素が複数スキーマに存在ルート要素の改名(ドメイン接頭辞)/Operation 名の見直しWSDL の operation 衝突を根本排除
WSDL を変更できないsvcutil /messageContract /serializer:DataContract で生成クライアント側でメソッド名重複を回避
複数サービスを同プロジェクトで参照/namespace で契約を集約・分割型名・名前空間の衝突を防止
既存コードにラップ API を揃えたいpartial で Facade を提供生成コード非改変で安全に表面 API を固定

補助コマンド集(覚えておくと便利)

# 詳細ログを見たいとき(問題特定用)
svcutil.exe /messageContract /serializer:DataContract /verbose <WSDL の URL>

# 既存アセンブリの型を再利用(名前のゆらぎを止める)

svcutil.exe /messageContract /r:Contracts.dll 

# バージョン依存の抑制(.NET 3.5 互換など)

svcutil.exe /messageContract /targetClientVersion:Version35  

結論

BizTalk 側の命名一意性と、.NET 側の svcutil /messageContract を軸に据えるだけで、代理クラスにおける重複メソッド問題は実務レベルで解消できます。WSDL を動かせない事情があっても、生成プロセスの制御partial ラッパで安全に運用可能です。まずは WSDL の operation 一意性チェックから着手し、同時に生成スクリプトを標準化しましょう。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次