WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

SqlDataSource とトランザクション

by WebSurfer 16. November 2010 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月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  July 2021  >>
MoTuWeThFrSaSu
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar