WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

2 つのテーブルの同時更新

by WebSurfer 21. August 2010 15:21

SQL Server DB の 2 つのテーブルから、INNER JOIN 句を用いてデータを抽出して GridView に表示し、それを編集・更新 (UPDATE) する Web アプリを考えます。

テーブルが 1 つなら、コードは一行も書かずに SqlDataSource を利用してウィザードベースでアプリを作成できますが、2 つのテーブルを一括更新する場合はそう簡単にはいきません。

それでも、できるだけ自力でコードを書かないで、ObjectDataSource と型付 DataSet + TableAdapter をウィザードベースで作って実現できないか考えて見ました。

ベースは MSDN ライブラリのチュートリアル「Walkthrough: Performing Bulk Updates to Rows Bound to a GridView Web Server Control 」で、そのテーブルを以下の 2 つのテーブルに変更することにします。(チュートリアルには日本語版もあったのですが、リンク切れになってしまいました)

CREATE TABLE [dbo].[table-1](
    [no] int NOT NULL,
    [name] nvarchar(50) NOT NULL,
 CONSTRAINT [PK_table-1] PRIMARY KEY CLUSTERED ([no] ASC)
)

CREATE TABLE [dbo].[table-2](
	[no] int NOT NULL,
	[tel] nvarchar(50) NOT NULL,
	[ban] nvarchar(50) NOT NULL,
 CONSTRAINT [PK_table-2] PRIMARY KEY CLUSTERED ([no] ASC)
)

まず、Visual Studio のウィザードで、型付 DataSet + TableAdapter(xsd ファイル)をつくります。以下のように、クエリビルダを使えば SELECT クエリは簡単に作成でき、それを基にした型付 DataSet も自動生成されます。

クエリビルダ

ただし、ここまでで自動生成された TableAdapter のコードには、更新に必要なメソッドは含まれていません。この先、以下のような方法で、2 つのテーブルを同時に UPDATE するコードを実装します。できるだけ自力でコードを書かないというのが条件です。

xsd ファイルを開いて、その TableAdapter にツールボックスから Query をドラッグ&ドロップ。クエリビルダで tablle-1 を UPDATE するクエリを作り、適当なメソッド名(例: UpdateQuery1)をつけて保存します。

同様に、table-2 を UPDATE するクエリを作り、適当なメソッド名(例:UpdateQuery2)をつけて保存します。結果は、以下のようになるはずです。

xsd ファイル

App_Code フォルダにクラスファイルを追加。UpdateQuery1 と UpdateQuery2 を使って2つのテーブルを UPDATE するメソッドを TableAdapter の partial class として実装します。ここは自力でコードを書く必要があります。以下のようになります。

using System;
using System.Data;
using System.Collections.Generic;
using System.Web;
using System.Transactions;
using System.ComponentModel;

namespace TwoTableDataSetTableAdapters
{
  public partial class DataTable1TableAdapter
  {
    [DataObjectMethod(DataObjectMethodType.Update)]
    public int UpdateTwoTables(int original_no, string name, string tel)
    {
      int returnValue;

      using (TransactionScope scope = 
        new TransactionScope(TransactionScopeOption.RequiresNew))
      {
        returnValue = this.UpdateQuery1(name, original_no);
        returnValue += this.UpdateQuery2(tel, original_no);
        scope.Complete();
      }
      return returnValue;
    }
  }
}

ObjectDataSource の「データソースの構成」で UPDATE メソッドに上記の partial class に作ったメソッドを選択します。

GridView を上記の ObjectDataSource に接続し、参考にした MSDN ライブラリのチュートリアルに従って、Template を編集し、コードを実装します。以下のようになります。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  // http://msdn.microsoft.com/ja-jp/library/aa992036(VS.80).aspx を参照
    
  private bool tableCopied = false;
  private DataTable originalDataTable;

  // 最初の行のバインディング中に、元のデータベース値のコピーが 
  // DataTable オブジェクトに格納され、さらにこのオブジェクトが 
  // ViewState に格納されます。
  protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
      if (!tableCopied)
      {
        originalDataTable = 
          ((DataRowView)e.Row.DataItem).Row.Table.Copy();
        ViewState["originalValuesDataTable"] = originalDataTable;
        tableCopied = true;
      }
    }
  }

  protected void UpdateButton_Click(object sender, EventArgs e)
  {
    originalDataTable = (DataTable)ViewState["originalValuesDataTable"];

    // GridView コントロールの行を反復処理し、各行に対してカスタムの 
    // IsRowModified 関数を呼び出します。
    foreach (GridViewRow r in GridView1.Rows)
    {
      if (IsRowModified(r))
      {
        GridView1.UpdateRow(r.RowIndex, false);
      }
    }

    // Rebind the Grid to repopulate the original values table.
    tableCopied = false;
    GridView1.DataBind();
  }

  // このプロシージャは、編集可能な各 TextBox コントロールの値と、
  // キャッシュされた DataTable オブジェクトに格納された値の文字列
  // 比較を実行します。行が変更されている場合は true を返します。
  protected bool IsRowModified(GridViewRow r)
  {
    int currentNo = Convert.ToInt32(GridView1.DataKeys[r.RowIndex].Value);
    string currentName = ((TextBox)r.FindControl("nameTextBox")).Text;
    string currentTel = ((TextBox)r.FindControl("telTextBox")).Text;
    // フィルタ基準と一致するすべての DataRow オブジェクトを主
    // キーの順に(主キーがない場合は追加された順に) 配列として
    // 取得します。
    DataRow row = 
      originalDataTable.Select(String.Format("no = {0}", currentNo))[0];

    if (!currentName.Equals(row["name"].ToString()))
    {
      return true;
    }
    if (!currentTel.Equals(row["tel"].ToString()))
    {
      return true;
    }

    return false;
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>無題のページ</title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:ObjectDataSource ID="ObjectDataSource1" 
      runat="server" 
      OldValuesParameterFormatString="original_{0}" 
      SelectMethod="GetData" 
      TypeName="TwoTableDataSetTableAdapters.DataTable1TableAdapter" 
      UpdateMethod="UpdateTwoTables">
      <UpdateParameters>
        <asp:Parameter Name="original_no" Type="Int32" />
        <asp:Parameter Name="name" Type="String" />
        <asp:Parameter Name="tel" Type="String" />
      </UpdateParameters>
    </asp:ObjectDataSource>
    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="no" 
      DataSourceID="ObjectDataSource1" 
      onrowdatabound="GridView1_RowDataBound">
      <Columns>
        <asp:BoundField 
          DataField="no" 
          HeaderText="no" 
          ReadOnly="True" 
          SortExpression="no" />
        <asp:TemplateField 
          HeaderText="name" 
          SortExpression="name">
          <ItemTemplate>
            <asp:TextBox ID="nameTextBox" 
              runat="server" 
              Text='<%# Bind("name") %>'>
            </asp:TextBox>
          </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField 
          HeaderText="tel" 
          SortExpression="tel">
          <ItemTemplate>
            <asp:TextBox ID="telTextBox" 
              runat="server" 
              Text='<%# Bind("tel") %>'>
            </asp:TextBox>
          </ItemTemplate>
        </asp:TemplateField>
      </Columns>
    </asp:GridView>    
    <asp:Button ID="UpdateButton" 
      runat="server" 
      onclick="UpdateButton_Click" 
      Text="Update" />    
  </div>
  </form>
</body>
</html>

上記のコードおよび TableAdapter の partial class は自力でコードを書く必要がありま���が、その他はすべて ウィザードベースでコードを書かずに実装できるはずです。

実行結果は、以下の画像にようになります。

実行時の画面

Tags: ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  June 2021  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar