SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

SQLXMLとシリアル化を使用して.NETオブジェクトをSQL Serverに保存する

UPDATEGRAMとXMLによるSQL Serverのデータ操作

  • X ポスト
  • このエントリーをはてなブックマークに追加

前回の記事で、SQLXMLとシリアル化を利用して、SQL Serverからオブジェクトを取得する方法を紹介しました。本稿では、SQLXMLに含まれるUPDATEGRAMを使用して、オブジェクトをSQL Serverに保存する方法を紹介します。

  • X ポスト
  • このエントリーをはてなブックマークに追加

はじめに

 前の記事『SQLXMLとシリアル化を利用してSQL Serverからオブジェクトを取得する』を発表した後、私は、同記事で設計したスキーマ定義ファイルと、SQLXMLに含まれるUPDATEGRAMという別の技法を使用して、オブジェクトをデータベースに保存し直す方法について調べました。

 この記事では、マイクロソフトが設計したこの技法について説明します。そして、シリアル化とカスタムクラス属性を使用し、SQL Serverに送信して自動生成文を実行できる適切なXMLストリームを形成する、UPDATEGRAMクラスを作成します。

UPDATEGRAM機能

 まず最初に、Microsoft SQLXMLのUPDATEGRAM機能の概要を紹介します。UPDATEGRAM機能の詳細は、オンラインヘルプ(マシンにSQLXMLライブラリがインストールされている場合)またはMSDNサイトで見ることができます。この概念を要約すると、UPDATEGRAMは、XMLドキュメントであると共にXMLスキーマ定義であり、SQLXMLライブラリによって使用され、レコードを更新、挿入、および削除するコマンドの作成と実行をSQL Serverにおいて自動化するものと言えます。

 各UPDATEGRAMは、主に、一連のSYNC要素によって形成されます。それぞれは、同じトランザクションコンテキストで実行される操作のセットです。各SYNC要素は、実行される操作セットごとにBEFORE要素とAFTER要素を含むことができます。レコードを配置したり作成したりするにはBEFORE要素を使用し、レコードへの変更を保持するにはAFTER要素を使用します。空のBEFORE要素を指定すると、挿入が実行されます。一方、空のAFTERは、削除操作に相当します。両方が存在する場合は、「UPDATE AFTER where BEFORE」のような状態が生成されますが、この記事ではこの状態を扱います。

データ更新用フォームの追加

 さて、前の記事のWindowsアプリケーションに戻り、指定のOrderのプロパティを変更し、データを更新してSQL Serverに戻す新しいフォームを追加しましょう。

 グリッドフォームからフォームを開くためのDoubleClickイベントハンドラを追加し、次のコードを追加します。

private void grd_orders_DoubleClick(object sender, EventArgs e)
   {
      Form f = new OrderForm(
         this.orders[this.grd_orders.CurrentRowIndex]);
      f.Visible=true;
   }

 Loadイベントの中で、Orderをコントロールにバインドします。

private void OrderForm_Load(object sender, System.EventArgs e)
   {
      this.lblOrderNum.Text = this.order.OrderID.ToString();

      //we don't want null dates,
      //instead the control will be set to current or default
      if(this.order.OrderDate!=DateTime.MinValue)
         this.dtpOrderDate.Value = this.order.OrderDate;
      if(this.order.RequiredDate!=DateTime.MinValue)
         this.dtpRequiredDate.Value = this.order.RequiredDate;

      this.txtShipAddress.Text = this.order.ShipAddress;
      this.txtShipCity.Text = this.order.ShipCity;
      this.txtShipName.Text = this.order.ShipName;
      this.txtShipZip.Text=this.order.ShipPostalCode;
      this.txtShipCountry.Text = this.order.ShipCountry;
   }

[Save]ボタンの実装

 Orderを編集できる状態になったので、[Save]ボタンの実装に進みましょう。Form内のコントロールから値を取得し、それをOrderオブジェクトに設定するメソッド(getChanges()など)を作成する必要があります。我々の目標は、古いOrderBeforeノード、変更後のOrderAfterノードとするUPDATEGRAMを生成し、SQL Serverに送信して、更新を自動的に行うことです。

 まずに、Before状態を取得するメソッドを定義する必要があります(オブジェクトの現在の状態がAfter状態になります)。そこで、バイナリシリアル化を使用して、オブジェクトの複製と比較を行うことにしました。これは、プロジェクト内のエンティティの基本クラスで使用される、私が作成した小さなヘルパークラスです。

public class CloneHelpers
   {
      public static object DeepClone(object source)
      {
         MemoryStream m = new MemoryStream();
         BinaryFormatter b = new BinaryFormatter();
         b.Serialize(m, source);
         m.Position = 0;
         return b.Deserialize(m);
            
      }
      public static bool DeepEquals(object objA,object objB)
      {
         MemoryStream serA = serializedStream(objA);
         MemoryStream serB = serializedStream(objB);
         if(serA.Length!=serA.Length)
            return false;
         while(serA.Position<serA.Length)
         {
            if(serA.ReadByte()!=serB.ReadByte())
               return false;
         }
         return true;

      }
      public static MemoryStream serializedStream(object source)
      {
         MemoryStream m = new MemoryStream();
         BinaryFormatter b = new BinaryFormatter();
         b.Serialize(m, source);
         m.Position = 0;
         
         return m;
      }
   }

 次に、基本のビジネスクラスを定義しましょう。

[Serializable]
   public abstract class BaseBusinessEntity 
   {
      public object DeepClone()
      {
         return CloneHelpers.DeepClone(this);
      }

   
      public bool DeepEquals(object obj)
      {
         return CloneHelpers.DeepEquals(this,obj);
      }
      
   }

[Serializable]
   public abstract class BaseBusinessEntityCollection : CollectionBase
   {
      public object DeepClone()
      {
         return CloneHelpers.DeepClone(this);
      }
   }

 OrderFormでは、操作するエンティティの初期状態を定義するコードとプロパティを設定できます。

public OrderForm(OrdersMgmt.Order ord)
   {
      //get the order from the grid form
      this.order=ord;
      //set the initial state
      this.orderBefore = (OrdersMgmt.Order) ord.DeepClone();

 この時点で、次のように考える方がいるかもしれません。初期状態のオブジェクトをXMLへとシリアル化してBeforeノードを作成し、オブジェクトのプロパティが変更されたら、オブジェクトをXMLへ再びシリアル化してAfterノードを取得する、というやり方です。

 ここで、UPDATEGRAMの生成を抽象化してみましょう。そして、XMLストリームを正しく形成および実行するプロパティを保持するクラスを設計します。

 このクラスを最も重要なプロパティであるXSDファイルでインスタンス化します。

public updategram(string schemaFile)
   {
      //set the current schema to use 
      //when sending updategram to SQLSERVER
      this.schemaFilePath = schemaFile;
      //initialize the state of the updategram
      //and open the xml document
      this.InitUpdgDocument();
      this.syncCollection = new Hashtable();
   }

 InitUpdgDocument()でXMLドキュメントが開始します(ここでは話を簡単にするためにdocumentオブジェクトを使用していますが、自分の技術レベルに合わせて自由に最適化できます)。

private void InitUpdgDocument()
   {
      if(this.updgDocument==null)
      {
         //create empty updategram
         updgDocument = new XmlDocument();
         updgDocument
            .LoadXml("<ROOT xmlns:"+prefix+"=\""+ns+"\"></ROOT>");

      }
   }

 syncCollectionは、初期状態をキーとして、かつ、実際の状態を値として、変更する必要があるすべてのオブジェクトを保持しています。UPDATEGRAMとフォームの初期状態が作成されたので、[Save]ボタンをクリックするとどうなるかを確認できます。

private void btnUpdate_Click(object sender, System.EventArgs e)
   {
      //get form values and set entity values
      this.getChanges();
      //compare to object and create new sync node if something changed
      updg.Process(this.orderBefore,this.order);
      //commit all sync nodes created
      updg.Commit();

      //set new initial state
      this.orderBefore = (OrdersMgmt.Order) this.order.DeepClone();

      //this.Close();
   }

 getChages()演算では、コントロールからorder値を設定し、UPDATEGRAMのprocessメソッドを呼び出してオブジェクトを比較し、2つの状態が異なる場合は新しいキー/値エントリをsyncCollectionに挿入します。この例の場合、操作しているオブジェクトは1つだけですが、複数のオブジェクトの変更も問題なく行われます。すべての更新をコミットできる状態になったら、updategramCommitメソッドを呼び出します。SQL Serverで実際の更新作業が実行されます。Commitメソッドのコードを見てみましょう。

public void Commit()
   {
      //cycle all syncs with original keys
      foreach(object objOriginal in this.syncCollection.Keys)
      {
         //get reference to changed object
         object objChanged = this.syncCollection[objOriginal];

         //initialize a the serializer provider
         UpdgXSerializer x = 
            new UpdgXSerializer(objOriginal,objChanged);
         //get the serializer for the before element
         this.beforeSerializer = 
            x.getUpdgSerializer(UpdategramElement.Before);
         //get the serializer for the after element
         this.afterSerializer = 
            x.getUpdgSerializer(UpdategramElement.After);

         //create the sync element
         this.createSync();

         //create the before element
         this.BeginUpdate(objOriginal);

         //create the after element
         this.EndUpdate(objChanged);
      }

      //verify that we have a document to submit
      if(this.updgDocument==null)
         return;


      //create the stream with the xml document
      MemoryStream ms;
      ms = new MemoryStream();
      this.updgDocument.Save(ms);
      ms.Position = 0;

      try
      {
         //execute the xml stream
         XmlReader results = 
            sqlxmlHelper.executeUpdateGram(this.schemaFilePath,ref ms);
      }
      catch(Exception ex)
      {
         throw new Exception(
            "Error while committing operations to DB, "+ex.Message,ex);
      }
      finally
      {
         //clean up after submit
         ms.Close();
      }

      this.syncCollection = new Hashtable();
      updgDocument = null;
   }

 既定のシリアル化では、すべてのOrderLinesが子ノードとして順々に含まれるXMLが形成されます。

<updg:before>
   <Order>
      <OrderID>10250</OrderID>
      ...
      <OrderLines>
         <OrderDetail>
            <Quantity>10</Quantity>
            <Discount>0</Discount>
            <Item>
               <ProductID>41</ProductID>
               <ProductName>Jack's New England Clam Chowder
               </ProductName>
               <UnitPrice>9.65</UnitPrice>
            </Item>
            <OrderID>10250</OrderID>
            <ProductID>41</ProductID>
         </OrderDetail>
         <OrderDetail>
            <Quantity>35</Quantity>
            <Discount>0.15</Discount>
            <Item>
               <ProductID>51</ProductID>
               <ProductName>Manjimup Dried Apples</ProductName>
               <UnitPrice>53</UnitPrice>
            </Item>
            <OrderID>10250</OrderID>
            <ProductID>51</ProductID>
         </OrderDetail>

次のページ
問題点

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Gianluca Nuzzo(Gianluca Nuzzo)

MCAD認定上級Webデベロッパー。Microsoft製品とXMLを使ったWebアプリケーションに関して、長年にわたる開発経験を持つ。メールアドレスはgianluca_nuzzo@aliceposta.it

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/213 2006/02/10 11:29

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング