WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

SqlDataSource とトランザクション

by WebSurfer 2010年11月16日 22:52

SqlDataSource + DetailsView による更新が成功したら、変更されたフィールド名、更新前/後の値、変更日時をログとして SQL Server データベースに残す処置をトランザクションを切って行う方法です。

更新前/後の値の取得と、SqlDataSorce コントロールによる UPDATE 操作と ADO.NET のコードによる INSERT 操作のトランザクションをどのように設定するかがポイントだと思います。

まず、更新前/後の値ですが、これらは DetailsView.ItemUpdating イベントのハンドラにおいて、引数の DetailsViewUpdateEventArgs オブジェクトから Keys, OldValues, NewValues プロパティによって取得できます。

トランザクションの開始および SqlTranscation オブジェクトの取得、SqlCommand.Transaction プロパティのへの SqlTranscation オブジェクトの設定は、SqlDataSource.Updating イベントで行います。イベントハンドラの引数 SqlDataSourceCommandEventArgs から SqlConnection, SqlCommand オブジェクトへの参照を取得できますので、それらを用いて設定します。

ログを残すための INSERT 操作は、SqlDataSource.Updated イベントのハンドラで行います。イベントハンドラの引数 SqlDataSourceStatusEventArgs オブジェクトの Exception, AffectedRows プロパティを用いて、UPDATE 操作で例外が発生しなかったことと更新が行われたことが判定できます。

例外の発生がなく更新が行われたと判定されたら、ログを残すための操作(データベースへの INSERT 操作)を行います。INSERT 操作は、更新されたフィールドが複数の場合、同じ数だけ行うことになります。 INSERT 操作の途中で失敗した場合は、SqlDataSource による UPDATE 操作まで巻き戻すことになります。

そのために、引数 SqlDataSourceStatusEventArgs オブジェクトから、SqlDataSource が UPDATE 操作に用いた SqlConnection, SqlCommand, SqlTransaction オブジェクトへの参照を取得し、それらを用いて、SqlDataSource が行う UPDATE 操作と同じコネクション/トランザクションで INSERT 操作を行うように設定します。

サンプルコードは以下のとおりです。

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

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

<script runat="server">

  IOrderedDictionary keys;
  IOrderedDictionary oldValues;
  IOrderedDictionary newValues;
    
  protected void SqlDataSource2_Updating(object sender, 
    SqlDataSourceCommandEventArgs e)
  {
    SqlCommand command = (SqlCommand)e.Command;
    SqlConnection connection = command.Connection;
    connection.Open();
    SqlTransaction tx = connection.BeginTransaction();
    command.Transaction = tx;
  }
    
  protected void SqlDataSource2_Updated(object sender, 
    SqlDataSourceStatusEventArgs e)
  {
    SqlCommand command = (SqlCommand)e.Command;
    SqlConnection connection = command.Connection;
    SqlTransaction tx = command.Transaction;
    if (e.Exception == null && e.AffectedRows == 1)
    {
      string insertQuery = 
        "INSERT INTO History " + 
        "(FieldName, NewValue, OldValue, DateAndTime) " +
        "VALUES (@FieldName, @NewValue, @OldValue, @DateAndTime)";
      SqlCommand sqlCom = 
       new SqlCommand(insertQuery, (SqlConnection)connection);
      sqlCom.Transaction = (SqlTransaction)tx;
      sqlCom.Parameters.Add("@FieldName", SqlDbType.NVarChar);
      sqlCom.Parameters.Add("@NewValue", SqlDbType.NVarChar);
      sqlCom.Parameters.Add("@OldValue", SqlDbType.NVarChar);
      sqlCom.Parameters.Add("@DateAndTime", SqlDbType.DateTime);
      try
      {
        foreach (DictionaryEntry entry in oldValues)
        {
          if ((string)entry.Value != 
            (string)newValues[entry.Key])
          {
            sqlCom.Parameters["@FieldName"].Value = 
              (string)entry.Key;
            if (newValues[entry.Key] == null)
            {
              sqlCom.Parameters["@NewValue"].Value = DBNull.Value;
            }
            else
            {
              sqlCom.Parameters["@NewValue"].Value = 
                (string)newValues[entry.Key];
            }
            if (entry.Value == null)
            {
              sqlCom.Parameters["@OldValue"].Value = DBNull.Value;
            }
            else
            {
              sqlCom.Parameters["@OldValue"].Value = 
                (string)entry.Value;
            }
            sqlCom.Parameters["@DateAndTime"].Value = DateTime.Now;
            sqlCom.ExecuteNonQuery();
          }
        }
        tx.Commit();
      }
      catch (Exception)
      {
        if (tx != null)
        {
          tx.Rollback();
        }
        throw;
      }
      finally
      {
        connection.Close();
      }            
    }
    else
    {
      if (tx != null)
      {
        tx.Rollback();
      }
      connection.Close();
    }

    DropDownList1.DataBind();
  }

  protected void DetailsView1_ItemUpdating(object sender, 
    DetailsViewUpdateEventArgs e)
  {
    keys = e.Keys;
    oldValues = e.OldValues;
    newValues = e.NewValues;
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:SqlDataSource ID="SqlDataSource1" 
      runat="server" 
      ConnectionString="<%$ ConnectionStrings:MyDB %>" 
      SelectCommand="SELECT [id], [name] FROM [table]">
    </asp:SqlDataSource>
    <asp:DropDownList ID="DropDownList1" 
      runat="server" 
      DataSourceID="SqlDataSource1" 
      DataTextField="name" 
      DataValueField="id" 
      AutoPostBack="True">
    </asp:DropDownList>
    <hr />
    <asp:SqlDataSource ID="SqlDataSource2" runat="server" 
      ConnectionString="<%$ ConnectionStrings:MyDB %>" 
      SelectCommand=
        "SELECT [id], [name], [price], [memo] 
        FROM [table] 
        WHERE ([id] = @id)"             
      UpdateCommand=
        "UPDATE [table] 
        SET [name] = @name, [price] = @price, [memo] = @memo 
        WHERE [id] = @original_id" 
      OnUpdated="SqlDataSource2_Updated" 
      OnUpdating="SqlDataSource2_Updating" 
      OldValuesParameterFormatString="original_{0}">
      <SelectParameters>
        <asp:ControlParameter ControlID="DropDownList1" 
          Name="id" 
          PropertyName="SelectedValue" 
          Type="Int32" />
      </SelectParameters>
      <UpdateParameters>
        <asp:Parameter Name="name" Type="String" />
        <asp:Parameter Name="price" Type="Decimal" />
        <asp:Parameter Name="memo" Type="String" />
        <asp:Parameter Name="original_id" Type="Int32" />
      </UpdateParameters>
    </asp:SqlDataSource>
    <asp:DetailsView ID="DetailsView1" 
      runat="server" 
      AutoGenerateRows="False" 
      DataKeyNames="id" 
      DataSourceID="SqlDataSource2" 
      OnItemUpdating="DetailsView1_ItemUpdating" 
      EnableModelValidation="True">
      <Fields>
        <asp:BoundField DataField="id" 
          HeaderText="id" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="id" />
        <asp:BoundField DataField="name" 
          HeaderText="name" 
          SortExpression="name" />
        <asp:BoundField DataField="price" 
          HeaderText="price" 
          SortExpression="price" />
        <asp:BoundField DataField="memo" 
          HeaderText="memo" 
          SortExpression="memo" />
        <asp:CommandField ShowEditButton="True" />
      </Fields>
    </asp:DetailsView>    
  </div>
  </form>
</body>
</html>

Tags: ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar