Linq to SQL проблема параллелизма - PullRequest
1 голос
/ 28 сентября 2010

Hallo,

У меня есть веб-сервис, который имеет несколько методов, которые могут быть вызваны.Каждый раз, когда вызывается один из этих методов, я записываю вызов в статистическую базу данных, чтобы мы знали, сколько раз каждый метод вызывается каждый месяц, и среднее время обработки.

Каждый раз, когда я регистрирую статистические данные, я сначала проверяюбаза данных, чтобы увидеть, если этот метод для текущего месяца уже существует, если не строка создается и добавляется.Если он уже существует, я обновляю необходимые столбцы в базе данных.

Моя проблема в том, что иногда, когда я обновляю строку, я получаю исключение "Строка не найдена или изменена", и да, я знаю, что это потому, что строка имеетбыл изменен с тех пор, как я его прочитал.

Чтобы решить эту проблему, я безуспешно пытался использовать следующее:

  • Использовать использование вокруг моего текста данных.
  • Использовать использование вокругTransactionScope.
  • Используйте мьютекс, это не работает, потому что веб-служба (не уверен, я считаю, что это правильно) реплицируется на другом ПК для повышения производительности, но все еще использует ту же базу данных.
  • Разрешить конфликт параллелизма в исключении, это не работает, потому что мне нужно получить новое значение базы данных и добавить к нему значение.

Ниже я добавил код, используемый для регистрациистатистические данные.Любая помощь будет принята с благодарностью.

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 ]

1 голос
/ 29 сентября 2010

Я бы предложил SQL Server справиться с параллелизмом.

Вот как:

  1. Создайте хранимую процедуру, которая принимает ваши значения журнала(имя метода, месяц / дата и статистика выполнения) в качестве аргументов.

  2. В хранимой процедуре, прежде всего, получите блокировку приложения как здесь описаны , и здесь .Теперь вы можете быть уверены, что только один экземпляр хранимой процедуры будет запущен одновременно. (Отказ от ответственности! Я сам не пробовал sp_getapplock. Просто говорю. Но это кажется довольно простым, учитывая все примеры на паутинах.)

  3. Затем в хранимой процедуре запросите в таблице журнала запись текущего месяца для метода, чтобы определить, вставлять или обновлять, а затем выполнить вставку или обновление.

  4. Как выможет знать, что в VS вы можете перетаскивать хранимые процедуры из обозревателя серверов в конструктор DBML для быстрого доступа с помощью LINQ to SQL.

Если вы пытаетесь избежать хранимых процедур, тогда это решениеочевидно, не будет для вас, но я бы решил это легко и быстро.Надеюсь, это поможет!

0 голосов
/ 30 сентября 2010

Я не использовал 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

Я проверил это, вызывая методы моего веб-сервиса несколькими потоками одновременно, и каждый вызов регистрируется без проблем.

0 голосов
/ 29 сентября 2010

Если вы не хотите использовать подход хранимых процедур, грубый способ справиться с ним будет просто повторять это конкретное исключение. Например:

int maxRetryCount = 5;
for (int i = 0; i < maxRetryCount; i++)
{
   try
   {
     QueryAndUpdateDB();
     break;
   }
   catch(RowUpdateException ex)
   {
     if (i == maxRetryCount) throw;
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...