WCFなどのエンドポイントの設定はServiceReferences.ClientConfigファイルで行います。しかし、ServiceReferences.ClientConfigファイルは、XAP内に含める必要があります。このため、気軽にデプロイ環境に合わせてWebサービスの設定を変更することができません。
この問題を解決するために、Webサービスの設定をXMLファイルに記述し、このXMLファイルをXAPの外における機能を実装します。
機能の概要とクラス構成
Webサービス設定の外出し機能はSilverlightアプリ起動時に下記のような流れで動作します。
これで、サービスクライアントビルダーの初期化が完了します。これで、サービスクライアントビルダーがサービスクライアントを生成することができるようになります。
クラス構成を示します。
XMLファイルで設定する項目
XMLファイルの仕様
- 設置場所 … xapが配置されている場所
- 名前 … Client.config.xml
タグまたはアトリビュート | 概要 | 出現数 |
---|---|---|
config | 設定全般 | 1回 |
services | Webサービスの設定 | 1回 |
domain | Webサービスが配置されているサーバのURL指定 | 0回以上 |
domai.uri | Webサービスが配置されているサーバのURLの値 | 1回 |
service | Webサービスの接続設定 | 0回以上 |
service.id | Webサービス設定を識別する値 | 1回 |
service.substance | Webサービスの配置場所のドメインUriの差分の値 | 1回 |
service.binding | Webサービスとの通信方式の値(binaryを指定するとバイナリ通信、それ以外の値だとBasicHttpBinding) | 0または1回 |
service.timeOut | サーバと通信時のタイムアウト時間(指定なしでは、デフォルト値を使用) | 0または1回 |
service.maxReceivedMessageSize | 受信できる最大メッセージ サイズの値(指定なしでは、デフォルト値を使用) | 0または1回 |
XMLファイルの例を示す。
<config> <services> <domain uri="http://localhost:61730/"> <service id="TestService1" substance="WebService/TestService1.svc" binding="binary1" timeOut="6000000" maxReceivedMessageSize="2147483647"/> <service id="TestService2" substance="WebService/TestService2.svc" binding="binary"/> </domain> <domain uri="http://www.dummy.com/"> <service id="DummyService" substance="DummyService.svc"/> </domain> </services> </config>
ServiceInfoクラス
ServiceInfoクラスは、Webサービスごとの接続情報を保持するデータクラスです。
public class ServiceInfo { /// <summary>サービス情報の識別子</summary> public string ID { get; set; } /// <summary>domainとsubstanceを合成して作成したサービスのUri</summary> public EndpointAddress EndpointAddress { get; set; } /// <summary>サービスコール用Binding</summary> public Binding Binding { get; set; } }
ServiceConfigクラス
ServiceConfigクラスは、Webサービスの接続情報を保持する機能を提供するクラスです。このクラスもヘルパーメソッド以上の機能を持っていません。
public class ServiceConfig { private List<ServiceInfo> serviceInfos; public ServiceConfig(List<ServiceInfo> serviceInfos) { this.serviceInfos = serviceInfos; } /// <summary>サービスIDに対応したServiceInfoを取得する。</summary> /// <param name="serviceID"></param> /// <returns></returns> public ServiceInfo GetServiceInfo(string serviceID) { var q = from info in this.serviceInfos where info.ID == serviceID select info; if (q.Count() > 0) { return q.First(); } else { throw new ArgumentException("指定されたserviceIDに対応するServiceDataが見つかりませんでした。"); } } }
ServiceClientParserクラス
ServiceClientParserクラスは、ダウンロードしてきたXMLファイルからServiceConfigインスタンスを生成するクラスです。かなり長いのですが、ソースを乗せます。
public class ServiceClientParser { /// <summary>設定文字列からServiceConfigを生成する。</summary> /// <param name="configStr"></param> /// <returns></returns> public ServiceConfig Parse(string configStr) { List<ServiceInfo> serviceInfos = this.CreateServiceInfos(configStr); ServiceConfig config = new ServiceConfig(serviceInfos); return config; } /// <summary>ServiceInfoのリストを生成する。</summary> /// <param name="configStr"></param> /// <returns></returns> private List<ServiceInfo> CreateServiceInfos(string configStr) { List<ServiceInfo> serviceInfos = new List<ServiceInfo>(); // XML化し、services要素を取得する。 XDocument xml = XDocument.Parse(configStr); XElement configElement = xml.Element("config"); XElement services = configElement.Element("services"); foreach (XElement domain in services.Elements("domain")) { // ドメインURLを取得する。 string domainUri = domain.Attribute("uri").Value; foreach (XElement service in domain.Elements("service")) { ServiceInfo info = new ServiceInfo(); // サービスIDをセットする。 info.ID = service.Attribute("id").Value; // EndpointAddressをセットする。 string substance = service.Attribute("substance").Value; info.EndpointAddress = this.GetEndpointAddress(domainUri, substance); // Bindingをセットする。 info.Binding = this.GetBinding(service); // リストにServiceInfoインスタンスを追加する。 serviceInfos.Add(info); } } return serviceInfos; } /// <summary>EndpointAddressを取得する。</summary> /// <param name="domainUri"></param> /// <param name="substance"></param> /// <returns></returns> private EndpointAddress GetEndpointAddress(string domainUri, string substance) { EndpointAddress address = new EndpointAddress(domainUri + substance); return address; } /// <summary>Bindingを取得する。</summary> /// <param name="service"></param> /// <returns></returns> private Binding GetBinding(XElement service) { // バインディングのタイプと最大受信サイズを取得する。 string bindingType = this.GetBindingType(service); int? maxReceivedMessageSize = this.GetMaxReceivedMessageSize(service); Binding binding; if (bindingType == "binary") { // バイナリの場合、CustomBindingでBindingを生成する。 BindingElementCollection elements = new BindingElementCollection(); elements.Add(new BinaryMessageEncodingBindingElement()); HttpTransportBindingElement htbe = new HttpTransportBindingElement(); if (maxReceivedMessageSize.HasValue) { // 最大受信サイズを設定する。 int size = maxReceivedMessageSize.Value; htbe.MaxReceivedMessageSize = size; htbe.MaxBufferSize = size; } elements.Add(htbe); binding = new CustomBinding(elements); } else { // bindingTypeがbinary以外の場合、BasicHttpBindingでBindingを生成する。 BasicHttpBinding basicHttpBinding = new BasicHttpBinding(); if (maxReceivedMessageSize.HasValue) { // 最大受信サイズを設定する。 int size = maxReceivedMessageSize.Value; basicHttpBinding.MaxReceivedMessageSize = size; basicHttpBinding.MaxBufferSize = size; } binding = basicHttpBinding; } long? timeOutValue = this.GetTimeOutValue(service); if (timeOutValue.HasValue) { // タイムアウトを設定する。 TimeSpan ts = TimeSpan.FromMilliseconds(timeOutValue.Value); binding.OpenTimeout = ts; binding.SendTimeout = ts; binding.CloseTimeout = ts; binding.ReceiveTimeout = ts; } return binding; } /// <summary>バインディングタイプを取得する。</summary> /// <param name="service"></param> /// <returns></returns> private string GetBindingType(XElement service) { XAttribute bindingAttribute = service.Attribute("binding"); string bindingType = string.Empty; if (bindingAttribute != null) { bindingType = bindingAttribute.Value; } return bindingType; } /// <summary>最大受信メッセージサイズを取得する。</summary> /// <param name="service"></param> /// <returns></returns> private int? GetMaxReceivedMessageSize(XElement service) { string attributeName = "maxReceivedMessageSize"; return (int?)this.GetValue(service, attributeName); } /// <summary>タイムアウト時間を取得する。</summary> /// <param name="service"></param> /// <returns></returns> private long? GetTimeOutValue(XElement service) { string attributeName = "timeOut"; return this.GetValue(service, attributeName); } /// <summary>XML要素から属性の値を取得する。</summary> /// <param name="service"></param> /// <param name="attributeName"></param> /// <returns></returns> private long? GetValue(XElement service, string attributeName) { XAttribute att = service.Attribute(attributeName); if (att != null) { long timeOut; if (long.TryParse(att.Value, out timeOut)) { return timeOut; } else { return null; } } else { return null; } } }
明日以降に続く
長くなってしまったので、次の記事に続きます。