c# - باستخدام المعاملات أو SaveChanges(خطأ)و AcceptAllChanges()؟




entity-framework transactions (4)

لقد تم التحقيق في المعاملات ويبدو أنهم يعتنون بأنفسهم في EF طالما تمرير false إلى SaveChanges() ثم استدعاء AcceptAllChanges() حالة عدم وجود أخطاء:

SaveChanges(false);
// ...
AcceptAllChanges();

ماذا لو ساء شيء ما؟ لا يجب علي التراجع ، أو بمجرد انتهاء الطريقتين ، هل انتهت المعاملة؟

ماذا يحدث لأي أعمدة indentiy التي تم تعيينها في منتصف الطريق من خلال الصفقة؟ أفترض إذا كان شخص آخر أضاف سجلاً بعد كتابي قبل أن أتعرض لسيئتي فهذا يعني أن هناك قيمة مفقودة للهوية.

هل هناك أي سبب لاستخدام الطبقة TransactionScope القياسية في الكود الخاص بي؟


Answers

لأن بعض قواعد البيانات يمكن أن تطرح استثناء في dbContextTransaction.Commit () حتى أفضل هذا:

using (var context = new BloggingContext()) 
{ 
  using (var dbContextTransaction = context.Database.BeginTransaction()) 
  { 
    try 
    { 
      context.Database.ExecuteSqlCommand( 
          @"UPDATE Blogs SET Rating = 5" + 
              " WHERE Name LIKE '%Entity Framework%'" 
          ); 

      var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
      foreach (var post in query) 
      { 
          post.Title += "[Cool Blog]"; 
      } 

      context.SaveChanges(false); 

      dbContextTransaction.Commit(); 

      context.AcceptAllChanges();
    } 
    catch (Exception) 
    { 
      dbContextTransaction.Rollback(); 
    } 
  } 
} 

مع كيان الإطار في معظم الوقت ، تكون SaveChanges() كافية. يؤدي هذا إلى إنشاء معاملة أو التضمين في أي معاملة محيطة ويقوم بكل الأعمال الضرورية في تلك المعاملة.

في بعض الأحيان على الرغم من أن SaveChanges(false) + AcceptAllChanges() مفيد.

المكان الأكثر فائدة لهذا هو في الحالات التي تريد فيها إجراء معاملة موزعة عبر سياقين مختلفين.

أي شيء من هذا القبيل (سيئة):

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save and discard changes
    context1.SaveChanges();

    //Save and discard changes
    context2.SaveChanges();

    //if we get here things are looking good.
    scope.Complete();
}

إذا نجح context1.SaveChanges() ولكن context2.SaveChanges() فشل يتم إحباط المعاملة الموزعة بالكامل. ولكن للأسف ، تجاهل "الكيان" بالفعل التغييرات في context1 ، لذا لا يمكنك إعادة التشغيل أو تسجيل الفشل بشكل فعال.

ولكن إذا قمت بتغيير التعليمات البرمجية لتبدو هكذا:

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save Changes but don't discard yet
    context1.SaveChanges(false);

    //Save Changes but don't discard yet
    context2.SaveChanges(false);

    //if we get here things are looking good.
    scope.Complete();
    context1.AcceptAllChanges();
    context2.AcceptAllChanges();

}

بينما يرسل استدعاء SaveChanges(false) الأوامر الضرورية إلى قاعدة البيانات ، لا يتم تغيير السياق نفسه ، بحيث يمكنك القيام بذلك مرة أخرى إذا لزم الأمر ، أو يمكنك استجواب ObjectStateManager إذا كنت تريد.

وهذا يعني أنه في حالة قيام المعاملة بالفعل بطرح استثناء يمكنك تعويضه ، إما عن طريق إعادة محاولة أو تسجيل حالة كل سياقات ObjectStateManager مكان ما.

شاهد my مدونتي للمزيد.


إذا كنت تستخدم EF6 (Entity Framework 6+) ، فقد تغير ذلك لمكالمات قاعدة البيانات إلى SQL.
انظر: http://msdn.microsoft.com/en-us/data/dn456843.aspx

استخدم context.Database.BeginTransaction.

من MSDN:

using (var context = new BloggingContext()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
        try 
        { 
            context.Database.ExecuteSqlCommand( 
                @"UPDATE Blogs SET Rating = 5" + 
                    " WHERE Name LIKE '%Entity Framework%'" 
                ); 

            var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
            foreach (var post in query) 
            { 
                post.Title += "[Cool Blog]"; 
            } 

            context.SaveChanges(); 

            dbContextTransaction.Commit(); 
        } 
        catch (Exception) 
        { 
            dbContextTransaction.Rollback(); //Required according to MSDN article 
            throw; //Not in MSDN article, but recommended so the exception still bubbles up
        } 
    } 
} 

بعد الكثير من الانسحاب من الشعر اكتشفت أن حلقات foreach كانت الجناة. ما يجب أن يحدث هو استدعاء EF ولكن إعادته إلى IList<T> من ذلك النوع الهدف ثم قم IList<T> حلقة على IList<T> .

مثال:

IList<Client> clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
   var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
    // ...
}




c# entity-framework transactions