【问题标题】:How to lock out concurrent users from accessing a page related to data from a database如何锁定并发用户访问与数据库中的数据相关的页面
【发布时间】:2013-10-30 00:50:14
【问题描述】:

我有一个用 C#.NET 编写的应用程序,用户可以在其中输入、编辑和删除访问使用 SQL Server 创建的数据库的事件。该应用程序使用搜索页面将搜索查询中的各种行填充到数据网格中,然后一旦选择,用户就可以按下更新按钮,然后进入另一个页面,将数据填充到表单中进行编辑,即我的问题在哪里。我正在尝试实现一种锁定机制,以便当用户访问数据库中的一行时,另一个用户将无法拉出同一页面。目前,我已经为页面调用的每个存储过程实现了几个事务,认为这可以解决问题,但是,当页面加载时,每个存储过程都被单独调用,然后在填充数据后关闭,所以没有办法通过那里锁定。我的下一个想法是,尝试在此特定页面加载时将其锁定给并发用户。填充页面的主存储过程调用 IncidentID 来检索数据,那么有没有办法检查该 ID 是否在 Page_Load 中使用?我仍然是入门级,我的肩膀很大,所以我通过这里和其他网页学习了很多这种实现,但我还没有找到任何帮助......代码很长但如果您需要任何参考,请告诉我。 TIA

编辑:这是一些代码。我正在使用 SSMS,因此为我添加了分号。这些是测试,但具有确切的语法...... 用数据填充表单的查询

USE [Test_Live]
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_SelectIncident_ByCaseNumberUpdate]
(
    @IncidentID int
)
AS
BEGIN TRANSACTION
SELECT i.IncidentID,
i.PersonID,
i.BusInfoID,
i.TypeOfIncident,
i.DateOfIncident,
/* etc. etc. */
FROM tblTestIncident i WITH(ROWLOCK)
left JOIN tblTestPerson p on i.PersonID = p.PersonID
/* some other joins are here */
WHERE i.IncidentID = @IncidentID
COMMIT

用数据填充表单的 C# 代码。 GetConnection() 方法位于页面正在使用的另一个 .dll 文件中。它通过 ConfigurationSettings.AppSettings 返回连接字符串。

protected void Page_Load(object sender, EventArgs e)
{
    this.gIncidentID = Convert.ToInt32(Utils.decryptQueryString(this.Request.QueryString["iid"]));
    try
    {
        this.lblCurrentUser.Text = this.Page.User.Identity.Name.ToUpper();
    }
    catch (Exception ex)
    {
       this.lblErrors.Text = this.lblErrors.Text + "Unexpected exception in Load: " + ex.Message + ". ";
    }
    this.populateFormWithData(this.gIncidentID);
}

private void populateFormWithData(int incidentID)
{
   SqlConnection connection = this.GetConnection();
   SqlParameter sqlParameter = new SqlParameter("@IncidentID", SqlDbType.Int);
   sqlParameter.Direction = ParameterDirection.Input;
   sqlParameter.Value = (object)incidentID;
   SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
   sqlDataAdapter.SelectCommand = new SqlCommand();
   DataSet dataSet = new DataSet();
   sqlDataAdapter.SelectCommand.Connection = connection;
   sqlDataAdapter.SelectCommand.CommandText = "sp_SelectIncident_ByCaseNumberUpdate";
   sqlDataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
   sqlDataAdapter.SelectCommand.Parameters.Add(sqlParameter);
   sqlDataAdapter.Fill(dataSet, "Incident");
   if (dataSet.Tables["Incident"].Rows.Count > 0)
   {
      //code that fills in all fields within the page
   }
   else
   {
      this.lblCaseNumberData.ForeColor.Equals((object)"Red");
      this.lblCaseNumberData.Text = "No Case data found!";
   }
   //this is where I think the problem is as to why the transactions aren't working
   connection.Close();
}

我希望这会有所帮助...

【问题讨论】:

  • 根据您的问题及其固有的复杂性,请发布您尝试过的内容。
  • 刚刚添加了一些代码以便更好地理解。感谢您的回复。

标签: c# asp.net sql-server locking


【解决方案1】:

像这样锁定数据可能会有问题。用户可以保持页面打开并离开一天。 数据将被锁定,直到它们返回。您最好在表中添加一个 TimeStamp 列,并在更新之前检查它是否已更改。如果时间戳发生变化,通知用户并重新加载数据。

查看示例:

SQL Server optimistic locking - Returning changed timestamp value

时间戳/行版本信息:

http://msdn.microsoft.com/en-us/library/ms182776%28v=sql.105%29.aspx

【讨论】:

  • 感谢您的回复。实际上,我已经打算用它自己的部分来实现时间戳功能,这是另一个规范。如果用户打开时间过长,Intranet 应用程序已经将其启动。这种“锁定”程序正是他们想要的。有什么办法吗?
  • 您可以在表中添加 LockedBy 列。 Update Table set LockedBy = @UserID where IncidentID = @IncidentID and LockedBy is null 如果锁定失败(LockedBy 不为空)回滚/返回。完成后将 LockedBy 设置回 null。我建议在更新时执行此操作而不是读取,因为表可以在更新后立即解锁。
  • 回复有点晚,忙于工作。这就是我们要做的。我们在存储用户名的数据库中设置了一个标记列,默认为空,然后在编辑案例的页面加载时我们检查它是否为空,如果不是,则返回一条消息,返回该列中的值,然后设置只读字段。非常感谢你的帮助。实施需要一段时间,但它现在正在工作。非常整洁的东西!
猜你喜欢
  • 2016-03-20
  • 1970-01-01
  • 2011-06-09
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
  • 1970-01-01
  • 1970-01-01
  • 2012-04-03
相关资源
最近更新 更多