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>