VisualStudio で開発しているプロジェクト内で WebService を利用する場合、
「サービス参照の追加」を利用してサービスにアクセスするためのクライアントクラスを自動生成すると便利です。
そうすると、app.config / web.config に WebService の接続に関する設定が出力され、
プログラム実行時に動的に読み込まれてサービスクライアントを初期化します。
<system.serviceModel>
<bindings>
<customBinding>
<binding name="CustomSoapBinding" openTimeout="00:01:00" receiveTimeout="00:01:00">
<textMessageEncoding messageVersion="Soap12" />
<httpTransport maxReceivedMessageSize="4096000" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://hogehoge.net/Service/Endpoint1"
binding="customBinding" bindingConfiguration="CustomSoapBinding"
contract="Endpoint1.Endpoint1" name="Service1Port" />
</client>
</system.serviceModel>
プロジェクト内に「サービス参照の追加」をした状態
1つのプロジェクトで利用する場合はこれにて終了なのですが、
複数のプロジェクトで同じ WebService にアクセスするとき、
DLL に サービスクライアントを用意して、各プロジェクトはそのDLLを参照して WebService にアクセスしたくなりました。
やりたいことは、こんなイメージ
このとき問題になったのが、この部分です。
app.config / web.config に WebService の接続に関する設定が出力され、
プログラム実行時に動的に読み込まれてサービスクライアントを初期化します。
こういう風にしないといけないみたい
DLL を参照している各プロジェクトの app.config / web.config 全てに対して
WebService に接続するための設定を用意しなければなりません。
・・・・・いやだ!!
サービスクライアントDLLを参照するたびに app.config に設定を転記しないといけないのはとてもめんどうです。
私が求めた理想形
これを実現するためにメソッド
「サービス参照の追加」でサービスクライアントを追加したプロジェクトのアセンブリ( DLL )と同じ名前の設定ファイルを読みに行くメソッド。
public static T createServiceClient<T>()
where T : class
{
Type tt = typeof(T);
T ret = null;
// [このメソッドが格納されたアセンブリ].dll.config を読み込む
var assembly = typeof(ClientFactory).Assembly.GetName().EscapedCodeBase;
var configPath = System.IO.Path.Combine(new Uri(assembly).LocalPath);
if (System.IO.File.Exists(configPath))
{
// 設定ファイルが見つかったら解析
var config = System.Configuration.ConfigurationManager.OpenExeConfiguration(configPath);
var smsg = System.ServiceModel.Configuration.ServiceModelSectionGroup.GetSectionGroup(config);
ChannelEndpointElement en = null;
foreach (ChannelEndpointElement e in smsg.Client.Endpoints)
{
// ジェネリックで指定されたクラスと同じ Name の Endpoint を検索する
if (e.Name.Contains(tt.Namespace.Split('.').Last()))
{
en = e;
break;
}
}
// 上の検索で見つかった Endpoint のバインディング設定を取得する
var elm = smsg.Bindings.CustomBinding.Bindings[en.BindingConfiguration];
var binding = new System.ServiceModel.Channels.CustomBinding();
elm.ApplyConfiguration(binding); // カスタムバインディング定義から、このEndpoint で使用するバインディング情報を割り当てる
// ここまで読み込んできた Endpoint の設定で、サービスクライアントオブジェクトを初期化します。
ret = (T)tt.GetConstructor(new Type[] { typeof(System.ServiceModel.Channels.Binding),
typeof(System.ServiceModel.EndpointAddress)})
.Invoke(new object[] { binding,
new System.ServiceModel.EndpointAddress(en.Address) });
}
else
{
// 設定ファイルが見つからない時は、既定のコンストラクタで初期化する
// この場合、このメソッドを呼び出しているアセンブリの設定ファイル (app.config /web.config )で
// サービスクライアントオブジェクトが初期化されます。
ret = (T)tt.GetConstructor(Type.EmptyTypes).Invoke(null);
}
return ret;
}
メソッドを利用する側のコード
void Hogehoge()
{
var client = createServiceClient<ServiceClientClassName>();
.
.
.
}
引数にパスを受け取るようにして、任意のファイル名で Binding の設定を保存することもできるかと思います。