PDA

View Full Version : سوال: چگونگی کنترل خطا در ClientDataSet



Arghavan_Reza
چهارشنبه 18 اردیبهشت 1387, 17:20 عصر
در برنامه ای از ClientDataSet استفاده شده. جهت کنترل خطاهای SQL و هدایت کاربر چند سوال مطرح است:
*** فرض اول : 2 رکورد از بانک اصلاح شده است و فقط رکورد دوم دچار خطا شده است.
*** فرض دوم : رکورد سوم فعال است.
*** فرض سوم : اطلاعاتی از نام ClientDataSet دچار خطا شده نداریم.

1 - میخواهیم رکورد فاقد خطا (رکورد اول) قبل از از اعلان خطای رکورد دومی ثبت شود.
2 - هنگام اعلان خطا رکورد خطادار (رکورد دوم) فعال شود.

با تشکر.

Arghavan_Reza
شنبه 21 اردیبهشت 1387, 11:29 صبح
اساتید فن، مدیر گروه، مسئول سایت و ...
آیا این مسئله قابل حل نیست؟

vcldeveloper
شنبه 21 اردیبهشت 1387, 15:38 عصر
سوال شما برای من خیلی واضح نیست. ApplyUpdate بالافاصله با اولین خطایی که رخ داد متوقف نمیشه، بلکه تک تک تغییرات در بانک ذخیره میشند، هر تغییری که با خطا مواجه بشه، رکورد مربوط به آن نگه داری میشه تا به اطلاع کاربر برسه. بعد از اتمام کار ثبت تغییرات، رکوردهایی که با خطا مواجه شدند به کلاینت برگشت داده میشند تا در سمت کلاینت در Event-handler مربوط به رویداد OnReconcilError به وضعیتشان رسیدگی بشه. در اینجا برنامه نویس میتونه پیغامی به کاربر نمایش بده و بهش اطلاع بده که این رکوردها ثبت نشدند. علاوه بر اون میتونه دلیل ثبت نشدن اون رکوردها و مقادیر فعلی اون رکوردها در سمت سرور را به کاربر نمایش بده، و به کاربر اجازه بده که تصمیم بگیره چکاری باید انجام بشه (مثلا لغو ثبت این رکوردها، ویرایش مجدد رکوردها و...)، یا اینکه برنامه نویس خودش در این مورد تصمیم بگیره.
توضیحات راهنمای دلفی درمورد ApplyUpdates و OnReconcilError را مطالعه کنید. برای هر دو یک مثال هم ارائه شده.

Arghavan_Reza
شنبه 21 اردیبهشت 1387, 19:16 عصر
جناب آقای کشاورز، با سلام،
در مورد مشکل اول:

1 - میخواهیم رکورد فاقد خطا (رکورد اول) قبل از از اعلان خطای رکورد دومی ثبت شود.
من هم نظر شما را دارم :

ApplyUpdate بالافاصله با اولین خطایی که رخ داد متوقف نمیشه، بلکه تک تک تغییرات در بانک ذخیره میشند، هر تغییری که با خطا مواجه بشه، رکورد مربوط به آن نگه داری میشه تا به اطلاع کاربر برسه.
اما رکوردهای بدون خطا، برخلاف انتظار ثبت نمی شوند. ضمنا تمام حالات زیر بررسی و تست شده است.

TResolverResponse = (rrSkip, rrAbort, rrApply, rrIgnore)
TReconcileAction = (raSkip, raAbort, raCorrect, raCancel, raRefresh)

به عبارتی در مورد مشکل اول، انتظار می رود دستورات ذیل به درستی انجام شود:
1 - برخی از رکوردها توسط کاربر اصلاح شده.
2 - توسط کاربر دستور ثبت داده شده (ApplyUpdate(-1.
3 - برخی از رکوردهای اصلاح شده دچار خطا شده اند.
4 - کاربر از ادامه اصلاحات منصرف شود.
5 - با بازخوانی اطلاعات انتظار داریم اطلاعات بدون خطا ثبت شده باشند ولی هیچ کدام در بانک ذخیره نشده اند!!!

با تشکر از رهنمودهای شما...

vcldeveloper
شنبه 21 اردیبهشت 1387, 22:20 عصر
وسط کاربر دستور ثبت داده شده (ApplyUpdate(-1.
چرا مقدار -1 را به تابع پاس دادید؟! -1 یعنی اصلا مهم نیست خطایی پیش بیاد یا نه. باید 0 را به تابع پاس می دادید.

Arghavan_Reza
یک شنبه 22 اردیبهشت 1387, 12:47 عصر
ApplyUpdates(MaxErrors)
MaxErrors indicates the maximum number of errors that the provider should allow before prematurely stopping the update operation.
Set MaxErrors to –1 to indicate that there is no limit to the number of errors.

ملاحظه می فرمایید که پارامتر درست تنظیم شده.

ApplyUpdates returns the number of errors it encountered. Based on this return value and the setting of MaxErrors, successfully applied records are removed from the client dataset’s change log. If the update process is aborted before all updates are applied, any unapplied updates remain in the change log.
پس مشکل از چیست؟
متشکر...

vcldeveloper
یک شنبه 22 اردیبهشت 1387, 20:07 عصر
پس مشکل از چیست؟
می تونید یه Sample که کاری که می خواید انجام بدید را انجام بده، بزارید که بشه مشکلتون رو بررسی کرد؟

Arghavan_Reza
چهارشنبه 25 اردیبهشت 1387, 18:49 عصر
استاد گرامی،
در مورد مشکل اول:
وقتی نمونه ای کوچک با جدولی ساده درست میکنم نتیجه ی کار مورد قبول و انتظاره. ولی وقتی همین نمونه را روی جدول اصلی تست میکنم مشکل به قوت خودش باقیست. در بانک هم تنها چیزی که کنترل اطلاعات را در تریگر انجام میده در زیر نوشته ام:


CREATE TRIGGER [Account_Update] ON [dbo].[Account]
FOR INSERT, UPDATE
AS
IF EXISTS (SELECT [add].ID
FROM Accounting_Doc_Detail [add]
JOIN Inserted I ON [add].LocalID = I.LocalID AND [add].Account_ID = I.ParentID)
BEGIN
RAISERROR ('این حساب دارای گردش است و نمی تواند زیر حساب داشته باشد.', 16, 1)
ROLLBACK TRAN
END

نمیدانم اشکال از چیست!

Arghavan_Reza
چهارشنبه 25 اردیبهشت 1387, 18:52 عصر
چگونگی کنترل خطا در ClientDataSet

--------------------------------------------------------------------------------

در برنامه ای از ClientDataSet استفاده شده. جهت کنترل خطاهای SQL و هدایت کاربر چند سوال مطرح است:
*** فرض اول : 2 رکورد از بانک اصلاح شده است و فقط رکورد دوم دچار خطا شده است.
*** فرض دوم : رکورد سوم فعال است.
*** فرض سوم : اطلاعاتی از نام ClientDataSet دچار خطا شده نداریم.

1 - میخواهیم رکورد فاقد خطا (رکورد اول) قبل از از اعلان خطای رکورد دومی ثبت شود.
2 - هنگام اعلان خطا رکورد خطادار (رکورد دوم) فعال شود.

با تشکر.

در مورد اشکال دوم از دوستان کسی هست راهنمایی کنه؟

vcldeveloper
پنج شنبه 26 اردیبهشت 1387, 03:09 صبح
2 - هنگام اعلان خطا رکورد خطادار (رکورد دوم) فعال شود.
زمانی که خطایی در ApplyUpdates رخ میده، OnReconcileError فعال میشه. پارامتر Dataset در OnReconcileError حاوی مقادیر Deltaی ClientDataset هست؛ یعنی رکوردهایی که تغییر کردند. زمانی که تغییرات یک رکورد با موفقیت در بانک ثبت میشه، اون رکورد از دیتاست مربوط به Delta حذف میشه. زمانی که خطایی رخ میده و OnReconcileError فعال میشه، رکورد جاری در پارامتر Dataset بر روی رکوردی خطا را تولید کرده قرار میگیره. شما می تونید مقدار یکی از فیلدهای این رکورد را بخوانید (مثلا فیلد ID که معمولا توسط کاربر تغییر نمیکنه)، و بر روی ClientDataset با استفاده از متد Locate رکورد حاوی آن فیلد را پیدا کنید. به این ترتیب، رکورد جاری در ClientDataset برابر با رکوردی میشه که خطا را تولید کرده. دقت داشته باشید که رکورد جاری در پارامتر Dataset را در داخل OnReconcileError تغییر ندید!



وقتی نمونه ای کوچک با جدولی ساده درست میکنم نتیجه ی کار مورد قبول و انتظاره. ولی وقتی همین نمونه را روی جدول اصلی تست میکنم مشکل به قوت خودش باقیست.
من هم با یک جدول فرضی تست کردم و بدرستی کار کرد. از HandleReconcileError خود دلفی در OnReconcileError استفاده کردید که ببینید چه رفتاری از خودش نشان میده؟


در بانک هم تنها چیزی که کنترل اطلاعات را در تریگر انجام میده در زیر نوشته ام
ClientDataset شما مستقیم به بانک متصل نیست، بلکه به DatasetProvider متصل هست. شما می تونید جریان داده بین DatasetProvider و Dataset ،و Dataset با بانک را هم چک کنید:
1- در DatasetProvider هم رویدادی بنام OnUpdateError وجود دارد که اگر در عملیات Update خطایی بوجود بیاید، قبل از آنکه به ClientDataset برسد، فعال می شود. می تونید بررسی کنید که آیا DatasetProvider متوجه خطا می شود یا نه.
2- در Dataset ایی که داده ها را از بانک می گیرد و به DatasetProvider می دهد، از طریق رویداد OnPostError، بررسی کنید که آیا خطایی در این سطح ثبت می شود یا نه.
3- روند ارسال تغییرات به بانک و جواب بانک را توسط SQL Server Profiler دنبال کنید و ببینید چه پیام هایی بین برنامه و SQL Server رد و بدل می شود.

Arghavan_Reza
پنج شنبه 26 اردیبهشت 1387, 10:22 صبح
جناب آقای کشاورز؛ ضمن تشکر از وقتی که صرف نمودید،
در مورد مشکل دوم:

شما می تونید مقدار یکی از فیلدهای این رکورد را بخوانید (مثلا فیلد ID که معمولا توسط کاربر تغییر نمیکنه)، و بر روی ClientDataset با استفاده از متد Locate رکورد حاوی آن فیلد را پیدا کنید
این مورد را قبلا چک کرده ام ولی درست عمل نمیکند!
در مورد مشکل اول:
موارد ذیل نیز قبلا بررسی شده است:

1- در DatasetProvider هم رویدادی بنام OnUpdateError وجود دارد که اگر در عملیات Update خطایی بوجود بیاید، قبل از آنکه به ClientDataset برسد، فعال می شود. می تونید بررسی کنید که آیا DatasetProvider متوجه خطا می شود یا نه.
بله فعال میشود و پاسخهای rrAbort , rrSkip هر دو حالت بررسی شده است.

2- در Dataset ایی که داده ها را از بانک می گیرد و به DatasetProvider می دهد، از طریق رویداد OnPostError، بررسی کنید که آیا خطایی در این سطح ثبت می شود یا نه.

خیر ، چون به صورت Cache کار میکنم مسلم است که OnPostError فعال نمیشود.

3- روند ارسال تغییرات به بانک و جواب بانک را توسط SQL Server Profiler دنبال کنید و ببینید چه پیام هایی بین برنامه و SQL Server رد و بدل می شود.

این مشکل را بررسی نمودم. به نتیجه جالب زیر رسیدم:
*** اگر خطای پیش آمده در بانک (SQL Server) توسط Check Constraints گزارش شود، مشکل حل است و کلیه تغییرات قبل و بعد از خطا در بانک ذخیره می شود ولی اگر عبارت مندرج در Check Constraints به تریگر منتقل گردد فقط تغییرات بعد از بروز خطا در بانک ذخیره می شود و رکورد حاوی خطا و تغییرات قبل از آن RollBack میشود. شاید به جای RollBack Tran از دستور دیگری باید استفاده کنم! ولی به هر با توجه به نمونه ای که نوشته ام (پست 8) باید از تریگر استفاده کنم.

تشکر از راهنمایی دوستان....

vcldeveloper
پنج شنبه 26 اردیبهشت 1387, 14:30 عصر
در مورد مشکل دوم:
این مورد را قبلا چک کرده ام ولی درست عمل نمیکند!

من چک کردم:
- مقدار فیلد درست برگشت داده میشه.
- Locate رکورد مربوطه را پیدا میکنه، اما یک رکورد دیگه به عنوان رکورد فعال انتخاب میشه! مثلا بجای رکورد 3، رکورد 8 انتخاب میشه!
- اما در زمان بررسی، وقتی بعد از Locate یک ShowMessage گذاشتم که مقادیر را به من نشان بده، متوجه شدم که وقتی اجرای برنامه بوسیله ShowMessage متوقف میشه، رکورد فعال درست تغییر کرده، ولی وقتی ShowMessage بسته میشه، دوباره رکورد فعال تغییر میکنه! احتمالا خودش در زمان Update رکورد فعال را در ClientDataset تغییر میده.


شاید به جای RollBack Tran از دستور دیگری باید استفاده کنم!
ظاهرا همینطور هست و SQL Server تمام تغییرات شما را در یک Transaction قرار میده که با RollBack کردن آن، کل تغییرات به حالت قبل بر می گرده. برای اینکه ببینید چطور می تونید رفتار SQL Server را تغییر بدید، یا جایگزینی برای Rollback Tran پیدا کنید، بهتر هست که تاپیکی در این زمینه در بخش SQL Server ایجاد کنید.

Arghavan_Reza
پنج شنبه 26 اردیبهشت 1387, 16:58 عصر
من چک کردم:
- مقدار فیلد درست برگشت داده میشه.
- Locate رکورد مربوطه را پیدا میکنه، اما یک رکورد دیگه به عنوان رکورد فعال انتخاب میشه! مثلا بجای رکورد 3، رکورد 8 انتخاب میشه!
- اما در زمان بررسی، وقتی بعد از Locate یک ShowMessage گذاشتم که مقادیر را به من نشان بده، متوجه شدم که وقتی اجرای برنامه بوسیله ShowMessage متوقف میشه، رکورد فعال درست تغییر کرده، ولی وقتی ShowMessage بسته میشه، دوباره رکورد فعال تغییر میکنه! احتمالا خودش در زمان Update رکورد فعال را در ClientDataset تغییر میده.
تمام مراحلی که شما تست کرده اید ، من هم تست کرده ام و دقیقا همین اتفاقات افتاده و بنظرم منطقی نیست. دنبال راه حل خوب میگردم و گرنه راه حل دارم:
مثلا سیستم ؛ ID (یا فیلد کلید) را در OnReconcileError در متغیر یا آرایه ای ذخیره کند و پس از ApplyUpdate عمل Locate را انجام دهد و یا لیست رکوردهای دارای خطا را ذخیره و سپس در اختیار کاربر قرار دهد و با انتخاب کاربر عمل Locate شود.%