Linq to SQL并发问题

时间:2010-09-28 14:42:56

标签: linq-to-sql concurrency

喂,

我的Web服务有多个可以调用的方法。每次调用其中一个方法时,我都会将调用记录到统计数据库中,这样我们就知道每个方法每个月调用多少次以及平均处理时间。

每次我记录统计数据时,我首先检查数据库以查看当前月份的方法是否已经存在,如果不是,则创建并添加该行。如果它已经存在,我将所需的列更新到数据库。

我的问题是,有时当我更新一行时,我得到“未找到或未找到行”异常,是的,我知道这是因为自从我阅读以来该行已被修改。

要解决此问题,我尝试使用以下内容但未成功:

  • 在我的datacontext周围使用。
  • 在TransactionScope周围使用。
  • 使用互斥锁,这不起作用,因为Web服务(不确定我称之为正确的想法)是在不同的PC上复制出来以获得性能但仍然使用相同的数据库。
  • 解决异常中的并发冲突,这不起作用,因为我需要获取新的数据库值并为其添加值。

下面我添加了用于记录统计数据的代码。非常感谢任何帮助。

public class StatisticsGateway : IStatisticsGateway
{
    #region member variables
    private StatisticsDataContext db;
    #endregion

    #region Singleton
    [ThreadStatic]
    private static IStatisticsGateway instance;
    [ThreadStatic]
    private static DateTime lastEntryTime = DateTime.MinValue;

    public static IStatisticsGateway Instance
    {
        get
        {
            if (!lastEntryTime.Equals(OperationState.EntryTime) || instance == null)
            {
                instance = new StatisticsGateway();
                lastEntryTime = OperationState.EntryTime;
            }

            return instance;
        }
    }
    #endregion

    #region constructor / initialize
    private StatisticsGateway()
    {
        var configurationAppSettings = new System.Configuration.AppSettingsReader();
        var connectionString = ((string)(configurationAppSettings.GetValue("sqlConnection1.ConnectionString", typeof(string))));

        db = new StatisticsDataContext(connectionString);
    }
    #endregion

    #region IStatisticsGateway members
    public void AddStatisticRecord(StatisticRecord record)
    {
        using (db)
        {
            var existing = db.Statistics.SingleOrDefault(p => p.MethodName == record.MethodName &&
                                                              p.CountryID == record.CountryID &&
                                                              p.TokenType == record.TokenType &&
                                                              p.Year == record.Year &&
                                                              p.Month == record.Month);

            if (existing == null)
            {
                //Add new row
                this.AddNewRecord(record);
                return;
            }

            //Update
            existing.Count += record.Count;
            existing.TotalTimeValue += record.TotalTimeValue;

            db.SubmitChanges();
        }
    }

3 个答案:

答案 0 :(得分:1)

我建议让SQL Server处理并发问题。

以下是:

  1. 创建一个存储过程,接受您的日志值(方法名称,月份/日期和执行统计信息)作为参数。

  2. 在存储过程中,在其他任何事情之前,获取应用程序锁定described hereand here。现在您可以确保只有一个存储过程实例将一次运行。 (免责声明!我自己没有试过sp_getapplock。只是说。但是看起来相当简单,因为互联网上有所有的例子。)

  3. 接下来,在存储过程中,查询日志表以获取方法的当前月份条目,以确定是否插入或更新,然后执行插入或更新。

  4. 您可能知道,在VS中,您可以将存储过程从Server Explorer拖到DBML设计器中,以便使用LINQ to SQL轻松访问。

  5. 如果你试图避免存储过程,那么这个解决方案显然不适合你,但这就是我如何轻松快速地解决它。希望它有所帮助!

答案 1 :(得分:0)

如果您不想使用存储过程方法,那么处理它的粗略方法就是重试该特定异常。 E.g:

int maxRetryCount = 5;
for (int i = 0; i < maxRetryCount; i++)
{
   try
   {
     QueryAndUpdateDB();
     break;
   }
   catch(RowUpdateException ex)
   {
     if (i == maxRetryCount) throw;
   }
}

答案 2 :(得分:0)

我没有使用sp_getapplock,而是使用了HOLDLOCK和ROWLOCK,如下所示:

CREATE PROCEDURE [dbo].[UpdateStatistics] 
@MethodName as varchar(50) = null,
@CountryID as varchar(2) = null,
@TokenType as varchar(5) = null,
@Year as int,
@Month as int,
@Count bigint,
@TotalTimeValue bigint 

AS 开始     SET NOCOUNT ON;

BEGIN TRAN

    UPDATE dbo.[Statistics]
    WITH (HOLDLOCK, ROWLOCK)
    SET Count = Count + @Count
    WHERE MethodName=@MethodName and CountryID=@CountryID and TokenType=@TokenType and Year=@Year and Month=@Month
    IF @@ROWCOUNT=0
        INSERT INTO dbo.[Statistics] (MethodName, CountryID, TokenType, TotalTimeValue, Year, Month, Count) values (@MethodName, @CountryID, @TokenType, @TotalTimeValue, @Year, @Month, @Count)

COMMIT TRAN

END GO

我已经通过多个线程同时调用我的Web服务方法来测试它,并且每次调用都没有任何问题。