asp:SqlDataSource with ROWVERSION (TIMESTAMP)



我遇到。net(框架,w/WinForm和WebForms)/MS SQL项目,其中数据库中的重要表包含一个TIMESTAMP(又名ROWVERSION)列(称为tsModified),以防止并发问题。此外,该项目不允许应用层直接与表交互(相反,所有CRUD和业务逻辑必须通过存储过程完成)。

让我抓狂的一件事是如何使用SqlDataSource来解释UPDATE期间的TIMESTAMP列。

CRUD过程的基本形式如下:

CREATE PROC Customer_List
@pk_Customer INT = null
SELECT id, name, tsModified 
FROM Customer 
WHERE @pk_Customer IS NULL OR @pk_Customer = id;
CREATE PROC Customer_Update
@id INT,
@name varchar,
@tsMod TIMESTAMP
IF NOT EXISTS (SELECT TOP 1 1 FROM Customer where id=@id and tsModified=@tsModified)
Return; --actually RAISEERROR w/ a concurrency alert telling the user to refresh & try again
UPDATE Customer SET [param/value pairs] WHERE id = @id;

当然,您可以手动定义部分类和方法来解释tsModified,然后使用asp:ObjectDataSource,但这是额外的工作。我只是想方便地在表单上删除asp:SqlDataSource,然后继续我的一天。

但是…SqlDataSource不喜欢将TIMESTAMP作为参数。事实上,我确实花了好几天的时间研究如何使这个工作,并遇到了很多其他人有同样的问题。

我终于想通了。

在使用存储过程时,如何使用MS SQL ROWVERSION (TIMESTAMP)列和asp:SqlDataSource来处理并发性

设置SqlDataSource:

<asp:SqlDataSource ID="dsRegs" runat="server" OnUpdating="dsRegs_Updating" ConnectionString="[your connstr]" InsertCommand="RegulatoryAgency_Insert" InsertCommandType="StoredProcedure" SelectCommand="RegulatoryAgency_List" SelectCommandType="StoredProcedure" UpdateCommand="RegulatoryAgency_Update" UpdateCommandType="StoredProcedure">
<InsertParameters>
<asp:Parameter Name="RegulatoryCode" Type="String" />
<asp:Parameter Name="RegulatoryName" Type="String" />
<asp:Parameter Name="RegulatoryState" Type="String" />
</InsertParameters>
<SelectParameters>
<asp:Parameter Name="pk_RegulatoryAgency" Type="DBNull" />
</SelectParameters>
<UpdateParameters>
<asp:Parameter Name="pk_RegulatoryAgency" Type="Int32" />
<asp:Parameter Name="RegulatoryCode" Type="String" />
<asp:Parameter Name="RegulatoryName" Type="String" />
<asp:Parameter Name="RegulatoryState" Type="String" />
<asp:Parameter Direction="InputOutput" Name="tsModified" Type="Empty" />
</UpdateParameters>
</asp:SqlDataSource>

需要注意的重要事项是:

  1. 在UpdateParameters中,tsModified是TIMESTAMP值,Type="Empty"
  2. OnUpdating设置为dsRegs_Updating事件

后面的代码:

/// <summary>
/// When editing for this record/row begins in the grid, we need to get the primary key from the row, 
/// and then stuff the TIMESTAMP (tsModified) into a Session variable so it persists
/// </summary>
protected void gvRegs_StartRowEditing(object sender, DevExpress.Web.Data.ASPxStartRowEditingEventArgs e)
{
int pk = (int)e.EditingKeyValue;
var db = new myDataContext();
var ra = db.RegulatoryAgency_List(pk).First();
Session["tsModified"] = ra.tsModified;
}
/// <summary>
/// Before we call the database, convert the Session var back the original Linq-to-SQL type (System.Data.Linq.Binary), then
/// convert it to a (byte) array, and update the SqlDataSource parameter with the correct value.
/// </summary>
protected void dsRegs_Updating(object sender, SqlDataSourceCommandEventArgs e)
{
DbParameter dp = e.Command.Parameters["@tsModified"];
dp.Value = ((System.Data.Linq.Binary)Session["tsModified"]).ToArray();
}

在这个例子中,前面使用的是DevExpress ASPxGridView,但是数据绑定和事件在其他数据绑定控件上应该是相似的。当行编辑开始时,我们从数据库中提取记录的tsModified值,并将其放入Session变量中。然后SqlDataSource触发它的update事件,我们获取Session变量,将其转换回它的原始格式(在我的例子中是System.Data.Linq.Binary,因为这个示例使用的是Linq-to-SQL),最后一个技巧是不能以二进制、varbinary或字节的形式传递TIMESTAMP值——它必须以btype[]的形式发送,这由. toarray()负责。

有了这样的代码,我就能够通过SqlDataSource成功地执行SELECT、INSERT、UPDATE和DELETE操作,并且数据库中的tsModified (TIMESTAMP)值会按预期增加。

最新更新