شما اینجا هستید: خانه / مقالات آموزشی / کتابخانه ی Entity Framework Extended – ثبت تاریخچه تغییرات (Audit Log)

کتابخانه ی Entity Framework Extended – ثبت تاریخچه تغییرات (Audit Log)

این مقاله، آخرین مقاله از سری مقالات آموزشی کتابخانه ی Entity Framework Extended می باشد. در مطالب قبلی به بررسی ۳ قابلیت “ویرایش و حذف دسته ای” ، “ارسال همزمان Query ها” و “مکانیزم Caching” از این کتابخانه پرداختیم. در این مقاله قصد داریم تا با قابلیت “ثبت تاریخچه تغییرات” (یا همان Audit Log) از این کتابخانه آشنا شویم.

  • Audit Log چیست؟

Audit Log تاریخچه ی دقیق تغییرات یک موجودیت (Entity) می باشد. تمام اطلاعاتِ موجودیت (مانند تاریخ و زمان، کاربر تغییر دهنده، نحوه تغییر و … ) بعد از اعمال تغییرات، با عنوان یک Audit Log در سیستم ذخیره خواهد شد.

  • استفاده از Entity Framework Extended برای ثبت Audit Log

در حالت پیش فرض قابلیت Audit Log برای هیچکدام از Entity ها فعال نمی باشد. می توانید این قابلیت را برای Entity های مورد نیاز خود (در این مثال Province) فعال نمایید :

همچنین در صورتی که نیاز به ثبت تغییرات تمام Entity ها دارید، می توانید این پیش فرض را تغییر دهید :

برای ثبت Audit Log باید متد BeginAudit را قبل از شروع فرآیند ذخیره سازی فراخوانی کنید. در صورتی که این متد فراخوانی شود، تغییرات حاصل از SaveChages از طریق مشخصه ی LastLog قابل دسترسی می باشد :

اکنون از طریق شی LastLog می توانید به Audit Log ها دسترسی داشته باشید و آنها را در فایل و یا در دیتابیس ذخیره نمایید. شی LastLog دارای سه Property می باشد :

  1. Date : تاریخ و زمان اعمال تغییرات.
  2. Entities : لیستی از کلاس AuditEntity که شامل اطلاعات Entity های تغییر یافته می باشد.
  3. Username : نام کاربری اعمال کننده ی تغییرات.

همچنین در صورتی که قصد دارید Audit Log ها را در فایل ذخیره کنید، میتوانید از متد ToXml این کلاس استفاده نمایید.

 

  • ذخیره اتوماتیک Audit Log بعد از هر SaveChanges

در صورتی که در قسمت های مختلف و زیادی از کدتان نیاز به ذخیره و یا آپدیت داده ها داشته باشید، نیاز دارید که کدهای مربوط به ذخیره ی Audit Log را در همه ی این قسمت ها کپی کنید. برای جلوگیری از این کار می توانید روشی را پیاده سازی کنید تا بعد از هر SaveChanges ، روال ذخیره ی Audit Log به صورت اتوماتیک انجام گیرد.

ابتدا یک فیلد Private از نوع AuditLogger بر روی DbContext خود تعریف کنید و ان را در Constructor با فراخوانی متد BeginAudit مقدار دهی کنید :

این کار باعث می شود تا با هر بار استفاده از DbContext، عملیات ثبت تغییرات در حافظه به صورت اتوماتیک انجام گیرد. حال تنها کافی است بعد از اعمال تغییرات در دیتابیس، Audit Log مربوط را ذخیره نمایید. می توانید این کار را با Override کردن متد SaveChanges انجام دهید :

 

بروزرسانی ۱ : برای اطمینان از جهت ذخیره شدن Audit Log ها، میتوانید ذخیره سازی اطلاعات و ذخیره سازی لاگ ها را در یک Transaction انجام دهید :

 ذخیره سازی اطلاعات همراه با Log آنها در یک Transaction در اکثر موارد توصیه میشود (البته باید حجم لاگ ها خیلی کم باشد و گرنه برنامه به شدت کند میشود). چون در اکثر سیستم های اطلاعاتی تاریخچه ی تغییرات فایل ها بسیار مهم و حیاتی است و با این روش هیچ رکوردی بدون ثبت Log آن، ذخیره نخواهد شد.

درباره هادی احمدی

هادی احمدی
برنامه نویس، تحلیلگر و طراح نرم افزار که به فعالیت بر روی بسترهای نرم افزاری مایکروسافت مشغول هستم. علاقه مند به مباحث طراحی و معماری نرم افزار و همچنین پیاده سازی سیستم های اطلاعاتی پیچیده می باشم.

6 نظر

  1. رضا جان محمدی

    سلام
    این قابلیت خیلی خوبه!
    اما دوتا مشکل دارم که امیدوارم راه حل مناسبی داشته باشه.
    ۱- چطور میتونم نتیجه لاگ رو به ادمین برنامه نشون بدم، بدون اینکه بخوام خودم XML رو پردازش کنم و نشون بدم؟! چیز آماده ای پیدا نکردم!
    ۲-من می خوام Search هایی که تو برنامه انجام مشه هم Log کنم. که این نمیتونه. راه حلی داری؟
    مرسی

    • هادی احمدی

      سلام رضا جان
      در مورد سوال اول اینکه برای نشون دادن نتیجه ی لاگ نیازی به پردازش XML نداری، شیء LastLog روی AuditLogger اطلاعات مربوط به آخرین تغییر رو داره که میتونی ازش استفاده کنی و به ادمین برنامه نشون بدی (امیدوارم سوالت رو درست متوجه شده باشم).

      در مورد سوال دوم هم اینکه ذخیره کردن چیزی مثل Search خارج از حوزه ی AuditLog هست و بیشتر یک نوع EventLog محسوب میشه. ثبت EventLog ها در سیستم میتونه به عنوان یک CrossCutting در نظر گرفته بشه و در قالب یک Aspect پیاده سازی بشه. به طور کلی سه راه برای AOP وجود داره که میتونی داخل فریم ورک یا زیر ساخت های برنامه ات پیاده سازی کنی و استفاده کنی :

      راه اول (و راه ساده تر) ساخت یک Proxy روی کلاس هات و ذخیره ی Log ها قبل و بعد از اجرا شدن متد هاست. برای پیاده سازی راحت اینکار میتونی از Interceptor ها در IoC Container ها استفاده کنی. اکثر IoCC های معروف مثل Castle Windsor و Ninject و… این قابلیت رو دارن.

      راه دوم استفاده از IL Weaving هست که با کتابخانه هایی مثل PostSharp قابل پیاده سازی هست.

      راه سوم استفاده از Decorator که مسئولیت ثبت EventLog ها رو به عهده داره ( این راه تقریبا پر هزینه تره چون احتمالا باید توی Interfaceهات تغییرات بدی)

  2. سلام ، واقعا مطالب مفید و به درد بخوری روی سایت قرار دادید،ممنون،
    در مورد این قابلیت ، اگر بخوام این لاگ رو دوباره روی دیتابیس ذخیره کنم ، یکبار دیگه عملیات IO خواهم داشت که به نظرم روند کارو کند میکنه ، اگر راه حلی وجود داره ، دو عملیات ذخیره و درج لاگ با یک مراجع به دیتابیس انجام بشه ، ممنون میشم بفرمایید

    • هادی احمدی

      سلام، خواهش میکنم لطف دارید
      امیدوارم مطالب بهتون کمک کرده باشه.
      برای اینکار میتونید عملیات ذخیره شدن اطلاعات و ذخیره ی لاگ ها را در یک Transaction انجام دهید. نمونه کد به مطلب اضافه شد، به بخش “بروزرسانی ۱″ توجه کنید.

  3. درود بر دوست عزیزم هادی احمدی ،
    این روش بازهم از دوبار به دیتابیس رفتن جلوگیری نمی کند و علاوه بر اون overhead ترنزکشن بالاست که خودش باعث کندی میشه ، البته نیازی به استفاده از transactionscop هم نیست چرا که خود EF بروی یک Context این وظیفه رو برعهده میگیرد.نظرتون در مورد اینکه روی ی new instance of context لاگ سیستم زده بشه چیه ؟

    • هادی احمدی

      سلام بر دوست عزیزم
      بله حرف شما رو قبول دارم اما نکته اینه که برای اینکه واقعا با یک بار رفتن به دیتابیس همه ی اطلاعات رو ذخیره کنید، نیاز دارید تا قبل از صدا زدن SaveChanges اطلاعات مربوط به Log تغییرات رو داشته باشید و اون رو به Context اتچ کرده باشید. که متاسفانه این کار با این کتابخانه شدنی نیست و باید از روش های دیگر استفاده کنید. چون این کتابخانه فقط بعد از SaveChanges آبجکت LastLog را مقدار دهی میکند. به همین دلیل هم بنده در مثال از TransactionScope استفاده کردم.
      در صورتی که نخواهید از این کتابخانه استفاده کنید ، با استفاده از رویداد SavingChanges بر روی ObjectContext می تونید اینکار رو در یک RoundTrip انجام بدید. از لینک زیر استفاده کنید :
      ObjectContext.SavingChanges Event

      روشی که فرمودید رو هم دقیق متوجه منظورتون نشدم، یکم بیشتر و دقیق تر در موردش توضیح بدید.

دادن پاسخ بههادی احمدی بی خیال پاسخ!

آدرس ایمیلتان منتشر نمیشودگزینه های الزامی ستاره دار شده اند *

*

شما می‌توانید از این دستورات HTML استفاده کنید: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

رفتن به بالا