این مسئله همزمانی رو باید در موردش مطلب بخونید تا جزییاتش دستتون بیاد در ادامه کار مشکلی نخورین..
فرض کنید جدول زیر رو داریم:
CREATE TABLE [dbo].[MyTable2](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[ItsRowVersion] [timestamp] NOT NULL,
CONSTRAINT [PK_MyTable2] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
و روال زیر رو برای اپدیت این جدول (داخل این روال، همزمانی رو هندل و اگه این اتفاق افتاده باشه می تونیم خطایی رو صادر کنیم سمت کلاینت)
CREATE PROCEDURE [dbo].[usp_UpdateMyTable]
@pId INT,
@pName nvarchar(50),
@pItsRowVersion Timestamp
AS
BEGIN
--------------------------------------------------------------------
UPDATE MyTable
SET
Name = @pName
WHERE Id = @pId
-- علاوه بر مقايسه کليد اصلي جدول، مقدار يونيک
-- itsRowVersion
-- هم چک مي شه تا با پارامتر وردي - ک از سمت کلاينت اومده- يکي باشه
AND ItsRowVersion= @pItsRowVersion
-- اگه نتيجه ي اجراي دستور آپديت ،سطري رو تغيير داده يعني کار موفقيت اميز بوده
-- اگه سطري بدرستي اپديت شده باشه مقدار متغير سراسري @@ROWCOUNT >= 1
-- خواهد شد. ک در اينصورت بخش پايين اجرا نمي شه ديگه و نيازي ب هندل کردن
-- ConcurrencyConflict نمي باشد
--------------------------------------------------------------------
-- اگر ک اپديت اتفاق نيفتاده باشه، بايد اين مشکل همزماني رو بسته ب سياست کاريمون هندل کنيم
IF @@ROWCOUNT=0
BEGIN
-- ممکنه اصلا اين موجوديت رو يکي ديگه حذف کرده باشه
SELECT Name FROM MyTable WHERE Id=@pId
IF @@ROWCOUNT=0
BEGIN
-- اگه موجوديت مورد نظر يافت نشد يعني کاربري اون رو الان حذف کرده
-- مثلا مي تونين RaiseError
-- کنيد اينجا تا سمت برنامه کلاينت بقيه کار رو هندل کنيد.مثلا يک پيام ب کاربر نشون بدين
PRINT N'موجوديت مورد نظر توسط کاربر ديگري حذف شده'
-- RAISERROR ( ... )
END
-- کاربر ديگه اي رکورد مورد نظر رو اپديت کرده همزمان. باز هم بسته به سياست کاري تون مي تونين اقدام کنيد
ELSE
BEGIN
PRINT N'موجوديت مورد نظر توسط کاربر ديگري تغيير کرده'
-- RAISERROR ( ... )
END
END
END
GO
حالا فرض کنید همچین دیتایی رو در جدول داریم :
Id Name ItsRowVersion
1 ali1 0x00000000000007D5
2 reza1 0x00000000000007D6
(مقادیر مربوط ب ستون از جنس timestamp بصورت باینری خود-افزاینده هستن، ک خود dbms بعد از هر تغییری در رکورد ، مقدار ستون از جنس timestamp رو هم بروز می کنه و این مقدار یونیک هست)
حالا فرض کنیم شما در یک فرم، گریدی دارین و اینها رو دارین نمایش می دین.کاربر A سطر 1 رو واسه ویرایش انتخاب می کنه و فرم ویرایش باز می شه.
در این لحظه مقدار ستون ItsRowVersion=0x00000000000007D5 هست.
فرم ویرایش کماکان باز هست.
در همین حین کاربر B هم همین کار رو انجام می ده و موجودیت رو واسه ویرایش انتخاب می کنه.
هنوز هم مقدار ستون ItsRowVersion=0x00000000000007D5 هست. (چون تغییری در مقادیر ستونهای این سطر در سمت بانک اتفاق نیفتاده)
حالا کاربر B مقدار ستون name رو ب ali2 تغییر می ده و ذخیره می کنه (هنوز کاربر A فرم ویرایشش باز هست)
هنگام ذخیره اتفاق زیر می افته:
سمت برنامه کلاینت (فارغ از هر مدلی ک لایه رابط بانک اطلاعاتیتون رو نوشتین) موجودیتمون در حافظه داریم، روال مورد نظر (usp_UpdateMyTable) باید با مقادیر مناسب صدا زده شود ک برای موجودیت با id =1 این می شه :
EXEC dbo.usp_UpdateMyTable 1 , 'ali2' , 0x00000000000007D5
و در روال ما همانطور ک بالا دیدیم ، هنگام اپدیت چک می شه ک مقدار ItsRowVersion با پارامتر ورودی یکی باشد :
WHERE Id = @pId
-- علاوه بر مقايسه کليد اصلي جدول، مقدار يونيک
-- itsRowVersion
-- هم چک مي شه تا با پارامتر وردي - ک از سمت کلاينت اومده- يکي باشه
AND ItsRowVersion= @pItsRowVersion
در این لحظه مقدار ItsRowVersion = 0x00000000000007D5 و مقدار پارامتر ورودی روال ما هم @pItsRowVersion = 0x00000000000007D5 و یعنی برابر هستن و عمل اپدیت با موفقیت اتفاق می افته و از روال خارج می شه.
بعد از اپدیت این سطر ، مقدار ItsRowVersion هم توسط dbms بروز می شه ک نشون می ده مقدار سطر تغییر کرده:
Id Name ItsRowVersion
1 ali2 0x00000000000007D7
2 reza1 0x00000000000007D6
اوکی، کاربر B کارشو انجام داد و برنامه رو می بنده در حالی ک کاربر A فرم ویرایشش بازه هنوز. کاربر A کلید ذخیره رو می زنه و دستور زیر سمت بانک فرستاده می شه:
EXEC dbo.usp_UpdateMyTable 1 , 'ali2' , 0x00000000000007D5
چون موجودیت ali1 با id=1 در حافظه بوده، مقادیرش از اون لحظه هنوز تغییری نکرده. سمت بانک در روال ما، هنگام بررسی موجود بودن (و معتبر بودن) این موجودیت ، مقدار ItsRowVersion =0x00000000000007D7 و مقدار پارامتر ورودی ما، @pItsRowVersion =0x00000000000007D5 می باشد، ک برابر نیستن و Concurrency Conflict اتفاق می افته. در اون 2 3 تا if else هم بررسی های لازم انجام می شه و می تونین مثلا ب سمت برنامه کلاینت RAISERROR کنین تا سمت .net هندل کنید این قضیه رو.