2010-09-12 30 views
5

etkilenen satırlar için eqivalent Onay iş akışında hatırlatma e-postalarının tam olarak bir kez gönderilmesini sağlamak istiyorum.EF SqlCommand.ExecuteNonQuery

SqlCommand.ExecuteNonQuery ile dönüş değerini test ederek bunu sağlayabilir. EF kullanarak önerilen çözüm nedir? ObjectContext.SaveChanges belgelerine göre eşdeğer bir değer döndürmez.

SqlCommand örnek: (TransactionScope durumda SendMail başarısız DB güncellemesini geri almak için kullanılır.)

 

Dim sql = "UPDATE LeaveApprovalRequests SET State = 'Reminded'" & 
      " WHERE ID=3 AND State <>'Reminded'" 
Using scope As New TransactionScope 
    Using cnx As New SqlConnection(My.Settings.connectionString) 
     cnx.Open() 
     Dim cmd As New SqlCommand(sql, cnx) 
     If 1 = cmd.ExecuteNonQuery Then 
      SendMail() 
     End If 
     scope.Complete() 
    End Using 
End Using 
 

(bir rowversion mülkiyet Sabit = ConcurrencyMode kullanarak) iyimser eşzamanlılık etkinleştirme ve OptimisticConcurrencyException I yakalayarak Nesnenin gerçekten mağazada güncellenip güncellenmediğini belirleyebilirim. Artık TransactionScope (SendMail başarısız olursa DB güncellemesini geri almak için kullanılır) bir kilitlenme hatası atar. Neden?

 

Using scope As New TransactionScope 
    Using ctx As New ApprovalEntities 
    Try 
     Dim approval = ctx.LeaveApprovalRequests. 
     Where(Function(r) r.ID = 3 And r.State = "Created" 
     ).FirstOrDefault 
     If approval Is Nothing Then 
     Console.WriteLine("not found") 
     Exit Sub 
     End If 
     Threading.Thread.Sleep(4000) 
     approval.State = "Reminded" 
     ctx.SaveChanges() 
     SendMail() 
    Catch ex As OptimisticConcurrencyException 
     Exit Try 
    End Try 
    End Using 
    scope.Complete() 
End Using 
 

cevap

0

: o biraz daha çalışma olacak böylece potansiyel olarak Devlet dahil olmak üzere herhangi sahadaki değişim için OptimisticConcurrencyException alabilirsiniz s işi izler.

SaveChanges, güncellemeyi amaçladığı nesne sayısını, mağazada güncellenen numarayı döndürür. Bu nedenle, değişikliğin başarılı olup olmadığını belirlemek için OptimisticConcurrencyException ile birlikte kullanılmalıdır. Değiştirilmesi planlanandan başka özelliklerin bir OptimisticConcurrencyException neden olabileceğini dikkate almalısınız. Bir varlığı okumak ve aynı TransactionScope içinde güncelleştirmek bir kilitlenme neden olur. 1 dernek, çıkışta:

Bölünmüş ApprovalRequest varlık ikiye 1 ile: Ben aşağıdaki çözümü kullanabilirsiniz

benim Görev İçin

"Ben hatırlattı için oluşturulan gelen Devleti değiştirmek mümkün olursam Sadece bir e-posta" OptimisticConcurrencyException, SaveChanges ile TransactionScope içinde posta gönderin.

 

ApprovalRequests 
    ID (PK) 
    RequestedBy 
    ... 
    RowVersion (ConcurrencyMode=Fixed) 

ApprovalRequestStates 
    ApprovalRequest_ID (PK, FK) 
    State (ConcurrencyMode=Fixed) 
 
 

Using ctx As New ApprovalEntities 
    Dim approval = cxt.ApprovalRequests.Where ... 
    Dim state = ctx.ApprovalRequestStates. 
     Where(Function(r) r.ApprovalRequest_ID = approval.ID And r.State = "Created" 
     ).FirstOrDefault() 
    If state Is Nothing Then Exit Sub 
    state.State = "Reminded" 
    Threading.Thread.Sleep(3000) 
    Using scope As New TransactionScope 
     Try 
      ctx.SaveChanges() 
      SendMail() 
      scope.Complete() 
     Catch ex As OptimisticConcurrencyException 
      Exit Try 
     End Try 
    End Using 
End Using 
 

dikkat! Bir üst öğeyi, üst öğeyi kullanarak başvururken güncelleştirmek, üst öğenin DB güncellemesine de neden olur; bu durumda, istenmeyen bir OptimisticConcurrencyException öğesi atanır. Bu yüzden kullanmadım: ApprovalRequests.ApprovalRequestStates.State = "Hatırlatıldı"

2

Eh, Nitekim olarak etkilenen satırların tam sayısı) (ObjectContext.SaveChanges bir çağrı anlaşılabilir.

Eğer ObjectContext.SaveChanges documentation bir göz atacak olursak göreceksiniz:

public int SaveChanges()

  1. Dönüş Değeri: Bir Eklendi nesneler, Modifiye, veya SaveChanges çağrıldığında devlet Silinen sayısı.
  2. "SaveChanges, bir işlemin içinde çalışır. SaveChanges, bu işlemi geri alır ve kirli ObjectStateEntry nesnellerinden herhangi biri devam edemezse bir istisna atar."

(1) ve (2) temelde o SaveChanges için() çağrısı başarıyla tamamlandığını ve herhangi bir özel durum alamazsanız anlamı ardından dönüş değeri tam olarak nesne sayısını yansıttığını EF garanti Bu değiştirildi.

nedenle, yapmanız gereken tek şey: Yenile ClientWins ile çağrılır

try { 
    // Try to save changes, which may cause a conflict. 
    int num = context.SaveChanges(); 
    SendMail(); 
} 
catch (OptimisticConcurrencyException) { 
    //Refresh the entity, using ClientWins 
    context.Refresh(RefreshMode.ClientWins, yourEntityObject); 
    //SaveChanges again; 
    context.SaveChanges(); 
} 
, yeni damgası dahil veritabanında bu varlığın cari değerleri almak için bir sorgu yürütür. Bu nedenle, orijinal alan değerlerinin tümü en son veritabanı değerlerini yansıtacak şekilde güncelleştirildi, böylece SaveChanges() 'i bir kez daha güvenle deneyebiliriz.

Kişisel Soru ile Güncelleme:
iştir: Ben hatırlattı için oluşturulan gelen durumunu değiştirme olanağı bulduğum takdirde sadece geçecektir. Dolayısıyla, OptimisticConcurrencyException ile çalışırken SaveChanges üzerinden zorlamak mantıklı değildir. Durum değiştirilirken durum değiştirilirse hata işleyicisi çıkmalı ve aksi halde tüm görevi (okuma ve kaydetme) yeniden denemelisiniz. İyimser bir uzlaşı sadece bir devlet tarafından değil, bir RowVersion sütunu aracılığıyla etkinleştirilirse bunu nasıl yapabilirim?

Eh, bir satır değiştirilir her seferinde rowversion Alan otomatik olarak güncellenir senin durumunda böyle olduğunu, olur RowVersion üzerine eşzamanlılık Modu kapatın ve Devlet için, onu açarsanız iyi mülkiyet, böylece kod kadar basit olacaktır: Eğer eşzamanlılık Modu = (Bahsettiğiniz gibi) sadece rowversion özellik için Sabit ayarlamak istiyorsanız,
try { 
    context.SaveChanges(); 
    SendMail(); 
} 
catch (OptimisticConcurrencyException) { 
    // RowVersion Concurrency Mode = Fixed 
    // State Concurrency Mode = None 
    // If it reaches here it means that the State has been changed; 
    // so you do nothing except than throwing the exception 
    throw; 
} 

Ama, o zaman demek Ben soruma cevap Murtaza ile tartışma sonucunda
try { 
    ctx.SaveChanges(); 
    SendMail; 
} 
catch (OptimisticConcurrencyException) { 
    // RowVersion Concurrency Mode = Fixed 
    // State Concurrency Mode = None 
    // If it reches here it means that ANY/All field(s) has changed 
    // So we need to see if it was State: 
    ctx.Refresh(RefreshMode.ClientWins, approval); 
    ObjectStateEntry ose = ctx.ObjectStateManager.GetObjectStateEntry(approval); 
    string stateValue = ose.OriginalValues["State"].ToString(); 
    // If the value is still "Created" then something else should have changed, 
    // And caused the exception, so we can proceed with the Save: 
    if (stateValue == "Created") { 
     ctx.SaveChanges(); 
     SendMail; 
    } 
    else { 
     // Nope, it was state, so we throw the exception to the caller: 
     throw; 
    } 

+0

Bana göre bu belge, SaveChanges'in güncellemeyi amaçladığı nesnelerin sayısını döndürerek depoda güncellenen sayıyı döndürmediğini söylüyor. Bunu, SaveChanges() öğesinde bir kesme noktası ayarlayarak, SQL Server'daki değeri değiştirerek ve ardından devam ederek doğruladım. –

+0

Tam olarak. Ve herhangi bir istisna olmaksızın SaveChanges() * 'ı çağırabilirseniz, EF güncellemeyi amaçlayan nesne sayısının aslında mağazada güncelleneceğini veya bir istisna elde edeceğinizi garanti eder. –

+0

Maalesef, ilk yorumun doğru bir şekilde okunmamış (önemli kısmı hükmüne rağmen). İlk test vakam iyimser bir eşzamanlılık kullanmadı, ama gerçek dünya senaryolarımda çoğu durumda bunu mümkün kılacağım. Bu, TransactionScope'un neden attığı bir kilitlenme hatası verdi sorusunu bırakır. –