プログラミング Tips


クライアント・コールバックでデータベースの値を更新する方法

最終更新日 2006 年 3 月 30 日

download1.gif サンプル コードのダウンロード (aspnettips_ClientCallback4.msi, 271 KB)

※このサンプルをお使いいただくためには、Visual Studio 2005 が必要です。

クライアント・コールバックでデータベースの値を表示する方法

クライアント・コールバックの機能を使い、サーバーにあるデータベースから値を受け取る方法については、クライアント・コールバックでデータベースの値を表示する方法で紹介しました。ここで紹介したサンプルは、データベースの値を表示するのみでした。では、値を更新するときにはどうすればよいでしょうか。
そこで、今回は、クライアント・コールバックの機能を使い、値を送信しすることで、データベースの値を更新する方法について紹介します。

サンプル フォームの準備

サーバーで参照するデータベースは、図1 にあるものを使います。今回は、データベースを更新するため、クライアント・コールバックでデータベースの値を表示する方法で紹介したページに [更新] ボタンを追加したページを用意します(リスト1、リスト2、および図1)。[更新] ボタンを押したとき現在の値をサーバーへ送信してデータベースを更新するページを作成していきます。

図

図 1. IMAGE.mdb の IMGINFO テーブル

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="ClientCallback.aspx.vb" Inherits="_ClientCallback" %>

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

<html  >
<head runat="server">
    <title>Sample Page</title>
    <script type="text/javascript">
        function ChangeImage()
        {
            var lb = document.MyForm.ListBox1;
            var imageNo = lb.options[lb.selectedIndex].value
            CallServer(imageNo, "");
        }

        function ReceiveServerData(rcvValue)
        {
            // サーバーからの戻り値を配列に変換
            rcvValues = rcvValue.split(unescape("%0A"));
            // 列を添え字にした連想配列を作成
            var Row = new Object();
            for (count = 0; count < rcvValues.length; count=count+2){
                Row[rcvValues[count]] = rcvValues[count + 1];
            }
            // 値の表示
            if (Row["IMGPATH"] == ""){
                document.MyForm.Image1.src = "dummy.gif";
            }else{
                document.MyForm.Image1.src = Row["IMGPATH"];
            }
            document.MyForm.TextImageName.value = Row["NAME"];
            document.MyForm.TextImageColor.value = Row["COLOR"];
        }

    </script>
</head>
<body>
    <form id="MyForm" runat="server">
         <br />
        <table border="1">
            <tr>
                <td>
                    画像番号
                </td>
                <td>
                    <asp:ListBox ID="ListBox1" runat="server"></asp:ListBox>
                </td>
            </tr>
            <tr>
                <td>
                    イメージ
                </td>
                <td>
                    <asp:Image ID="Image1" runat="server" ImageUrl="dummy.gif" />
                </td>
            </tr>
            <tr>
                <td>
                    名称
                </td>
                <td>
                    <asp:TextBox ID="TextImageName" Runat="server"></asp:TextBox>
                </td>
            </tr>
            <tr>
                <td>
                    色
                </td>
                <td>
                    <asp:TextBox ID="TextImageColor" Runat="server"></asp:TextBox>
                </td>
            </tr>
            <tr>
                <td colspan="2" style="height: 28px" align="center">
                    <input type="Button" id="Button1" value="更新" onclick="UpdateData();"/>
                </td>
            </tr>
        </table>
  </form>
</body>
</html>

リスト1.ClientCallback.aspx

Partial Class _ClientCallback
    Inherits System.Web.UI.Page
    Implements System.Web.UI.ICallbackEventHandler

    Private returnValue As String
    Private myDataSet As Data.DataSet

    Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim cbReference As String

        cbReference = Page.ClientScript.GetCallbackEventReference(Me, "arg", "ReceiveServerData", "context")
        Dim callbackScript As String = ""
        callbackScript &= "function CallServer(arg, context) { " & cbReference & "} ;"
        Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), "CallServer", callbackScript, True)

        ' DataSet を作成し、データを読み込む
        myDataSet = New Data.DataSet
        myDataSet.Locale = Globalization.CultureInfo.InvariantCulture
        LoadDataSet()

        ' リスト ボックスの設定
        ListBox1.DataSource = myDataSet
        ListBox1.DataTextField = "NAME"
        ListBox1.DataValueField = "ID"
        ListBox1.DataBind()
        ListBox1.Attributes.Add("onchange", "ChangeImage();")

    End Sub

    Public Function OpenDataBase() As Data.OleDb.OleDbConnection

        Dim Conn As New Data.OleDb.OleDbConnection()

        Conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" + _
        AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "IMAGE.MDB"

        ' 取得したレコードを DataSet に設定
        Dim ds As New Data.DataSet
        ds.Locale = Globalization.CultureInfo.InvariantCulture
        Conn.Open()

        Return Conn

    End Function

    Public Sub LoadDataSet()

        Dim oleConn = OpenDataBase()

        Dim cmdSelect As New Data.OleDb.OleDbCommand("SELECT * FROM IMGINFO", oleConn)
        Dim oleDataAdapter As New Data.OleDb.OleDbDataAdapter(cmdSelect)

        oleDataAdapter.Fill(myDataSet)
        oleConn.Close()

        oleDataAdapter.Dispose()
        cmdSelect.Dispose()
        oleConn.Dispose()

    End Sub

    Public Function GetRecordString(ByVal keyId As String) As String

        Dim DataView As New Data.DataView(myDataSet.Tables(0))
        DataView.RowFilter = "ID=" + keyId

        Dim recordString As New StringBuilder()
        If DataView.Count > 0 Then
            ' 改行コードを区切りにして複数パラメータを渡す
            For count As Integer = 0 To DataView.Table.Columns.Count - 1
                ' フィールド名を設定
                recordString.Append(DataView.Table.Columns(count).ColumnName)
                recordString.Append(vbLf)      ' 改行コード
                ' 値を設定
                recordString.Append(DataView(0)(count).ToString)
                If count < DataView.Table.Columns.Count - 1 Then
                    recordString.Append(vbLf)  ' 改行コード
                End If
            Next
        End If

        DataView.Dispose()

        Return recordString.ToString

    End Function

    Public Sub RaiseCallbackEvent(ByVal eventArgument As String) Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent

        ' レコードの取得処理
        returnValue = GetRecordString(eventArgument)

    End Sub

    Public Function GetCallbackResult() As String Implements System.Web.UI.ICallbackEventHandler.GetCallbackResult

        Return returnValue

    End Function

End Class

リスト2.ClientCallback.aspx.vb

図

図 2. ClientCallback.aspx のデザイナ画面

データベースの更新処理を作成する

リスト3 のコード追加して、DataSet の値を元にデータベースを更新する処理を追加します。このコードを追加した後、myDataSet のレコードを更新してから UpdateDataSet を呼出すことでデータベースを更新することが出来ます。

Public Sub UpdateDataSet()

        Dim oleConn = OpenDataBase()

        Dim cmdSelect As New Data.OleDb.OleDbCommand("SELECT * FROM IMGINFO", oleConn)
        Dim oleDataAdapter As New Data.OleDb.OleDbDataAdapter(cmdSelect)
        Dim oleCmdBuilder = New Data.OleDb.OleDbCommandBuilder(oleDataAdapter)

        oleDataAdapter.Update(myDataSet)
        oleConn.Close()

        oleDataAdapter.Dispose()
        cmdSelect.Dispose()
        oleConn.Dispose()

    End Sub

リスト3.ClientCallback.aspx.vb に追加するコード

サーバー コールバック メソッドでレコードの更新処理を追加する

今回のサンプルでは、クライアント・コールバック処理は 1 つ実装していますが、データベースの値の取得と、更新の 2 種類の処理に分かれています。そこで、RaiseCallbackEvent で渡ってくるコールバック値が、改行文字で区切られていなかった場合は、画像番号のみ送られてきたと判断し、レコードの取得処理を実行します。改行文字で区切られていた場合は、レコードの全てのフィールド値が送られてきたと判断し、レコードの更新処理を実行します(リスト4 の赤文字)。
レコードの更新処理では、受け取ったコールバック値をフィールド毎に分離します。分離した値は、フィールド名を添え字とする連想配列に格納します。こうすることで、フィールド名を指定した連想配列から値を取得することが出来ます。

Public Sub RaiseCallbackEvent(ByVal eventArgument As String) Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent
        If eventArgument.IndexOf(vbLf) < 1 Then
            ' レコードの取得処理
            returnValue = GetRecordString(eventArgument)
        Else
            ' レコードの更新処理

            ' クライアントが渡してきたパラメータを配列に変換
            Dim arrArg As String() = eventArgument.Split(vbLf)

            ' 列を添え字にした連想配列を作成
            Dim hasRow As New Hashtable()
            For count As Integer = 0 To arrArg.Length - 2 Step 2
                hasRow(arrArg(count)) = arrArg(count + 1)
            Next

            ' 指定された ID のレコードが存在したら値を更新
            Dim dataRow As Data.DataRow() = myDataSet.Tables(0).Select("ID=" + hasRow("ID"))
            If dataRow.Length > 0 Then
                ' 値の更新
                Dim rowTarget As Data.DataRow = dataRow(0)
                rowTarget("NAME") = hasRow("NAME")
                rowTarget("COLOR") = hasRow("COLOR")
                UpdateDataSet()
            End If
            ' クライアントへ返す戻り値
            returnValue = ""
        End If
    End Sub

リスト4.ClientCallback.aspx.vb に追加するコード

クライアント コールバック メソッドでレコードの更新処理を追加する

[更新] ボタンが押されたとき呼出される UpdateData を追加します。ここでは、サーバーへ送信するレコードの値を各コントロールから取得し、サーバー側の GetRecordString で作成する文字列と同じフォーマットのものを作成します(リスト5)。

function UpdateData()
        {
            var lb = document.MyForm.ListBox1;
            if (lb.selectedIndex != -1){
                var imageNo = lb.options[lb.selectedIndex].value;
                var imageName = document.MyForm.TextImageName.value;
                var imageColor = document.MyForm.TextImageColor.value;
                var paramData = "";
                paramData += "ID" + unescape("%0A") + imageNo + unescape("%0A");
                paramData += "NAME" + unescape("%0A") + imageName + unescape("%0A");
                paramData += "COLOR" + unescape("%0A") + imageColor;
                CallServer(paramData, "");
            }
        }

リスト5.ClientCallback.aspx に追加するコード

また、サーバー側の処理が完了したときに呼ばれる ReceiveServerData では、サーバー側の RaiseCallbackEvent と同様に、取得と更新の 2 つの処理に分けます。今回のサンプルでは、更新完了時になにも行っていませんが、処理の完了を通知するクライアント スクリプト コードを追加することもできます(リスト6)。

function ReceiveServerData(rcvValue)
        {
            if (rcvValue.indexOf(unescape("%0A")) < 1){
                // レコードの更新完了
            }else{
                // レコードの取得完了
                // サーバーからの戻り値を配列に変換
                rcvValues = rcvValue.split(unescape("%0A"));
                // 列を添え字にした連想配列を作成
                var Row = new Object();
                for (count = 0; count < rcvValues.length; count=count+2){
                    Row[rcvValues[count]] = rcvValues[count + 1];
                }
                // 値の表示
                if (Row["IMGPATH"] == ""){
                    document.MyForm.Image1.src = "dummy.gif";
                }else{
                    document.MyForm.Image1.src = Row["IMGPATH"];
                }
                document.MyForm.TextImageName.value = Row["NAME"];
                document.MyForm.TextImageColor.value = Row["COLOR"];
            }
        }

リスト6.ClientCallback.aspx に追加するコード

完成した ClientCallback.aspx と ClientCallback.aspx.vb は、リスト7 およびリスト8 になり、実行結果は図3 のようになります。

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="ClientCallback.aspx.vb" Inherits="_ClientCallback" %>

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

<html  >
<head runat="server">
    <title>Sample Page</title>
    <script type="text/javascript">
        function ChangeImage()
        {
            var lb = document.MyForm.ListBox1;
            var imageNo = lb.options[lb.selectedIndex].value;
            CallServer(imageNo, "");
        }

        function ReceiveServerData(rcvValue)
        {
            if (rcvValue.indexOf(unescape("%0A")) < 1){
                // レコードの更新完了
            }else{
                // レコードの取得完了
                // サーバーからの戻り値を配列に変換
                rcvValues = rcvValue.split(unescape("%0A"));
                // 列を添え字にした連想配列を作成
                var Row = new Object();
                for (count = 0; count < rcvValues.length; count=count+2){
                    Row[rcvValues[count]] = rcvValues[count + 1];
                }
                // 値の表示
                if (Row["IMGPATH"] == ""){
                    document.MyForm.Image1.src = "dummy.gif";
                }else{
                    document.MyForm.Image1.src = Row["IMGPATH"];
                }
                document.MyForm.TextImageName.value = Row["NAME"];
                document.MyForm.TextImageColor.value = Row["COLOR"];
            }
        }

        function UpdateData()
        {
            var lb = document.MyForm.ListBox1;
            if (lb.selectedIndex != -1){
                var imageNo = lb.options[lb.selectedIndex].value;
                var imageName = document.MyForm.TextImageName.value;
                var imageColor = document.MyForm.TextImageColor.value;
                var paramData = "";
                paramData += "ID" + unescape("%0A") + imageNo + unescape("%0A");
                paramData += "NAME" + unescape("%0A") + imageName + unescape("%0A");
                paramData += "COLOR" + unescape("%0A") + imageColor;
                CallServer(paramData, "");
            }
        }

    </script>
</head>
<body>
    <form id="MyForm" runat="server">
         <br />
        <table border="1">
            <tr>
                <td>
                    画像番号
                </td>
                <td>
                    <asp:ListBox ID="ListBox1" runat="server"></asp:ListBox>
                </td>
            </tr>
            <tr>
                <td>
                    イメージ
                </td>
                <td>
                    <asp:Image ID="Image1" runat="server" ImageUrl="dummy.gif" />
                </td>
            </tr>
            <tr>
                <td>
                    名称
                </td>
                <td>
                    <asp:TextBox ID="TextImageName" Runat="server"></asp:TextBox>
                </td>
            </tr>
            <tr>
                <td>
                    色
                </td>
                <td>
                    <asp:TextBox ID="TextImageColor" Runat="server"></asp:TextBox>
                </td>
            </tr>
            <tr>
                <td colspan="2" style="height: 28px" align="center">
                    <input type="Button" id="Button1" value="更新" onclick="UpdateData();"/>
                </td>
            </tr>
        </table>
  </form>
</body>
</html>

リスト7.ClientCallback.aspx

Partial Class _ClientCallback
    Inherits System.Web.UI.Page
    Implements System.Web.UI.ICallbackEventHandler

    Private returnValue As String
    Private myDataSet As Data.DataSet

    Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim cbReference As String
        cbReference = Page.ClientScript.GetCallbackEventReference(Me, "arg", "ReceiveServerData", "context")
        Dim callbackScript As String = ""
        callbackScript &= "function CallServer(arg, context) { " & cbReference & "} ;"
        Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), "CallServer", callbackScript, True)

        ' DataSet を作成し、データを読み込む
        myDataSet = New Data.DataSet
        myDataSet.Locale = Globalization.CultureInfo.InvariantCulture
        LoadDataSet()

        ' リスト ボックスの設定
        ListBox1.DataSource = myDataSet
        ListBox1.DataTextField = "NAME"
        ListBox1.DataValueField = "ID"
        ListBox1.DataBind()
        ListBox1.Attributes.Add("onchange", "ChangeImage();")

    End Sub

    Public Function OpenDataBase() As Data.OleDb.OleDbConnection

        Dim Conn As New Data.OleDb.OleDbConnection()

        Conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" + _
        AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "IMAGE.MDB"

        ' 取得したレコードを DataSet に設定
        Dim ds As New Data.DataSet
        ds.Locale = Globalization.CultureInfo.InvariantCulture
        Conn.Open()

        Return Conn

    End Function

    Public Sub LoadDataSet()

        Dim oleConn = OpenDataBase()

        Dim cmdSelect As New Data.OleDb.OleDbCommand("SELECT * FROM IMGINFO", oleConn)
        Dim oleDataAdapter As New Data.OleDb.OleDbDataAdapter(cmdSelect)

        oleDataAdapter.Fill(myDataSet)
        oleConn.Close()

        oleDataAdapter.Dispose()
        cmdSelect.Dispose()
        oleConn.Dispose()

    End Sub

    Public Sub UpdateDataSet()

        Dim oleConn = OpenDataBase()

        Dim cmdSelect As New Data.OleDb.OleDbCommand("SELECT * FROM IMGINFO", oleConn)
        Dim oleDataAdapter As New Data.OleDb.OleDbDataAdapter(cmdSelect)
        Dim oleCmdBuilder = New Data.OleDb.OleDbCommandBuilder(oleDataAdapter)

        oleDataAdapter.Update(myDataSet)
        oleConn.Close()

        oleDataAdapter.Dispose()
        cmdSelect.Dispose()
        oleConn.Dispose()

    End Sub

    Public Function GetRecordString(ByVal keyId As String) As String

        Dim DataView As New Data.DataView(myDataSet.Tables(0))
        DataView.RowFilter = "ID=" + keyId

        Dim recordString As New StringBuilder()
        If DataView.Count > 0 Then
            ' 改行コードを区切りにして複数パラメータを渡す
            For count As Integer = 0 To DataView.Table.Columns.Count - 1
                ' フィールド名を設定
                recordString.Append(DataView.Table.Columns(count).ColumnName)
                recordString.Append(vbLf)      ' 改行コード
                ' 値を設定
                recordString.Append(DataView(0)(count).ToString)
                If count < DataView.Table.Columns.Count - 1 Then
                    recordString.Append(vbLf)  ' 改行コード
                End If
            Next
        End If

        DataView.Dispose()

        Return recordString.ToString

    End Function

    Public Sub RaiseCallbackEvent(ByVal eventArgument As String) Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent

        If eventArgument.IndexOf(vbLf) < 1 Then
            ' レコードの取得処理
            returnValue = GetRecordString(eventArgument)
        Else
            ' レコードの更新処理
            ' クライアントが渡してきたパラメータを配列に変換
            Dim arrArg As String() = eventArgument.Split(vbLf)
            ' 列を添え字にした連想配列を作成
            Dim hasRow As New Hashtable()
            For count As Integer = 0 To arrArg.Length - 2 Step 2
                hasRow(arrArg(count)) = arrArg(count + 1)
            Next

            ' 指定された ID のレコードが存在したら値を更新
            Dim dataRow As Data.DataRow() = myDataSet.Tables(0).Select("ID=" + hasRow("ID"))
            If dataRow.Length > 0 Then
                ' 値の更新
                Dim rowTarget As Data.DataRow = dataRow(0)
                rowTarget("NAME") = hasRow("NAME")
                rowTarget("COLOR") = hasRow("COLOR")
                UpdateDataSet()
            End If
            ' クライアントへ返す戻り値
            returnValue = ""
        End If

    End Sub

    Public Function GetCallbackResult() As String Implements System.Web.UI.ICallbackEventHandler.GetCallbackResult

        Return returnValue

    End Function

End Class

リスト8.ClientCallback.aspx.vb

図 3. ClientCallback.aspx の実行結果

Page view tracker