img

مدیریت تراکنش ها در Entity Framework 6

/
/
/

این مقاله در مورد مدیریت تراکنش هایی است که نیاز به خواندن آنها ندارید، با یک استثناء Entity Framework بطور پیش فرض کار درست را انجام خواهد داد. اما در .NET Framework 4 و بعد از آن ، می توانید کارهای بیشتری انجام دهید
اگر از NuGet برای اضافه کردن ورژن جدید Entity Framework (EF) به اپلیکیشن Microsoft .NET Framework 4 ( یا بعد از آن) استفاده می کردید، حالا می توانید از ویژگی های جدید مدیریت تراکنش که همراه با EF 6 عرضه می شوند، بهرمند شوید. از بین دو ویژگی که قصد داریم راجع به آنها صحبت کنیم، احتمالاً یکی برای شما اهمیت بیشتری دارد: مدیریت ارتباط های متناوب.
ویژگی دیگری که قصد داریم در مورد آن صحبت کنیم، قابلیت ادغام چندین بروز رسانی در یک تراکنش مجزاست که البته چندان جالب نیست. بطور پیش فرض ، EF فراخوانی های متد SaveChanges شما را در یک تراکنش قرار می دهد و این احتمالاً چیزیست که شما می خواهید: تمام بروزرسانی های شما به صورت یک گروه یا موفق می شوند یا همگی به شکست می انجامند. اگر به چیز متفاوتی نیاز دارید ( برای ادغام چندین فراخوانی SaveChanges در یک تراکنش یا برای ادغام کد EF با کد ADO.NET در یک تراکنش) ، اولین انتخاب شما باید آبجکت TransactionScope باشد ( البته تا زمانیکه از متدهای غیرهمزمان ADO.NET استفاده نمی کنید… ، حتی این کار در .NET Framework 4.5.1 نیز امکان پذیر است)

مدیریت تراکنش ها با ارتباط متناوب
پشتیبانی جدید EF برای مدیریت تراکنش ها با ارتباط های متناوب می تواند برای شما مفید باشد. موضوعی که این ویژگی مخاطب قرار می دهد این است که EF می تواند استثنا CommitFailedException را نشان دهد که می گوید تغییرات شما ممکن است عملی نشده باشند. حتی اگر تمام تغییرات شما عملی شده باشند و در حقیقت ذخیره شده باشند، EF ممکن است باز هم این استثناء را بروز دهد ( به خاطر داشته باشید که این پیام می گوید تغییرات شما ممکن است عملی نشده باشد). EF می تواند به اشتباه تغییرات ذخیره شده شما را گزارش کند، چون به دلیل قطع آنی ارتباط شبکه، EF پیامی از پایگاه اطلاعاتی که موفقیت انجام کار را گزارش دهد، دریافت نمی کند.
اگر این موضوع موجب نگرانی شما می شود، در EF 6.1 باید این کد را به متد سازنده یا کانستراکتور کلاسی که از DbConfiguration ارث می برد، اضافه نمایید:

Public Class MyConfiguration
Inherits DbConfiguration

Public Sub New
Me.SetTransactionHandler(SqlProviderServices.ProviderInvariantName,
Function() New CommitFailureHandler())
Me.SetExecutionStrategy(SqlProviderServices.ProviderInvariantName,
Function() New SqlAzureExecutionStrategy())
End Sub
End Class

این کلاس را در همان پروژه با آبجکت DbContext خود قرار دهید، آبجکت DbContext آن را پیدا کرده و از آن استفاده می کند.
این تغییر اطمینان می دهد که استثناء CommitFailedException تنها زمانی ظاهر می شود که تغییرات شما واقعاً انجام نگرفته باشند.
بهرحال این تغییر رایگان نیست و موجب می شود EF جدولی به نام _Transactions را به پایگاه اطلاعاتی شما بیافزاید و خواندن و نوشتن را در آن جدول انجام دهد تا تراکنش های ناتمام را بررسی نماید. این کار دو موضوع را پیش می آورد.
اول این که این امکان وجود دارد که EF تمام ورودیهایی که در جدول _Transactions می سازد را حذف نکند. برای این که مانع رشد بی قید و بند جدول شوید، باید کدی مانند کد زیر را به اپلیکیشن خود بیافزایید تا سطرهای باقیمانده در جدول حذف شوند. باید این کد را قبل از آنکه اپلیکیشن شما اولین بار از EF استفاده کند، فرا بخوانید ( در این مثال آبجکت DbContext یعنی AWEntitiesTransacted را فرا خواندیم):

Dim db As New AWEntitiesTransacted
Dim cfh As CommitFailureHandler
cfh = CommitFailureHandler.FromContext(db)
If cfh IsNot Nothing Then
cfh.ClearTransactionHistory()
End If

از آنجاییکه این تغییر I/O بیشتری را به اپلیکیشن شما می افزاید، باید در نظر داشته باشید که آیا این مشکل احتمالی آنقدر نگرانتان کرده که بخواهید متحمل سربار اضافی شوید یا خیر. اگر می خواهید فقط از یک تغییر در هر بروزرسانی مطلع شوید، و اگر EF خطای CommitFailedException را می دهد، پایگاه اطلاعاتی را بررسی کنید تا ببینید که آیا آن تغییر صورت گرفته است ( البته غالباً رخ نمی دهد). اگر این تغییر صورت نگرفته، پس تغییرات شما واقعاً ذخیره نشده اند.

ویژگی که هرگز به آن نیاز نخواهید داشت (احتمالاً)
ویژگی جدید دیگردر EF ، امکان ساختن یک تراکنش مجزاست که هر ترکیبی از کد ADO.NET ، آبجکت های DbContext و فراخوانی ها را در SaveChanges ادغام می کند (به شرطی که تمام این ورودیها با یک آبجکت Connection کار کنند).
فقط در صورتی از این تکنیک استفاده کنید که مشغول توسعه code-first EF هستید چراکه باید با یک کلاس اختصاصی DbContext با یک کانستراکتور یا سازنده بسازید که آبجکت Connection را بپذیرد. در آن کلاس باید کانستراکتور را برای کلاس DbContext فرا بخوانید و آبجکت Connection و False را انتقال دهید (False به DbContext می گوید که متعلق به این ارتباط یا connection نیست)
در ویژوال بیسیک آن کد اینگونه خواهد بود:

Public Class AWEntitiesTransacted
Inherits DbContext

Public Sub New(conn As SqlConnection)
MyBase.New(conn, False)

در C# همین کار را با کد زیر انجام دهید:
public class AWEntitiesTransacted: DbContext
{
public AWEntitiesTransacted(SqlConnection conn): base(conn, False)
{

برای مقید کردن دو SaveChanges در یک تراکنش مجزا ، ابتدا یک آبجکت Connection بسازید و آن را باز کنید. وقتی این Connection باز است می توانید متد BeginTransaction آن را فرابخوانید تا آبجکت Transaction را بازیابی کنید:

Dim conn As SqlConnection
Dim tran As SqlTransaction
conn = New SqlConnection(«…connection string…»)

conn.Open()
tran = conn.BeginTransaction

حال DbContext خود را بسازید و آبجکت Connection خود را از طریق کانستراکتور جدید خودتان به آن انتقال دهید ( ما آن را داخل بلوک Try…Catch قرار دادیم) :

Dim db As AWEntitiesTransacted
Try
db = New AdventureWorksLTEntitiesRevised(conn, False)

در نهایت به آبجکت DbContext می گویید که با انتقال آبجکت Transaction شما به متد UseTransaction (که می توانید خارج از خصوصیت DbContext Database آن را بیابید) ازآن استفاده کند :

db.Database.UseTransaction(tran)

حال وقتی SaveChanges را فرا می خوانید، هیچکدام از تغییرات شما عملی نمی شوند تا زمانیکه متد Commit آبجکت Transaction را فرابخوانید. همچنین می توانید از این آبجکت Transaction با آبجکت های ADO.NET Command استفاده کنید تا آنها را در این تراکنش بگنجانید. اگر اشتباهی رخ داد، می توانید متد Rollback متعلق به Transaction را فرابخوانید تا تغییرات خود را رد کنید ( یا اینکه فراخوانی متد Commit از Transaction را انجام ندهید).
مهم نیست با آبجکت Transaction چه کاری انجام می دهید ، اما هنگام انجام کار باید Connection را ببندید. اگر از بلوک Try…Catch استفاده می کنید ، کد شبیه این خواهد بود:

tran.Commit()
Catch
tran.Rollback()
Finally
conn.Close()
End Try

آخرین نکته: آنچه در این مقاله به شما پیشنهاد شد را می توانید به روش ساده تری نیز انجام دهید ، به این ترتیب که تمام کد را در یک بلوک Using که آبجکت TransactionScope را می سازد ، قرار دهید

 

نظر بدهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

It is main inner container footer text