EF5,

2016-03-23 7 views
5

Seçici alanları güncelleştiriliyorken Eşzamanlılık işlenemiyor Güncelleştirme varlıklarına EF5 ve Veri İlk yaklaşımını kullanıyorum. Başka varlıklar tarafından önerilen, yalnızca Varlıklar'daki değiştirilmiş özellikleri güncellemek için önerilen yaklaşımı kullanıyorum.EF5,

Oki işte buradaki senaryo Benim denetleyici çağrım Servis ile POCO nesneleriyle hizmet alıyor ve Hizmet'ten POCO nesneleri alıyor, Hizmet katmanı, DB'den varlık almak ve DB'de güncelleştirmek için EF5'i dahili olarak kullanan Veri katmanıyla konuşuyor.

Görünüm verileri, Hizmet katmanından alınan DTO nesnesinden denetleyici tarafından yüklenir. Kullanıcı, denetleyicideki DTO nesnesine eşlenen denetleyiciye (izleme MVC'si) denetleyiciye JSON verilerini görüntüle ve geri gönder. Denetleyici, DTO nesnesi (POCO) nesnesiyle Servis katmanına çağrı yapar. Hizmet, POCO nesnesini EF varlık nesnesine eşler ve EF nesnesinden geçen Veri katmanı (yani Deposu) Güncelleştirme yöntemini çağırır. Deposu'nda varolan varlığı DB'den getir ve ApplyCurrentvaluesValues ​​yöntemini çağır, sonra herhangi bir özellik değiştirilip değiştirilmediğini kontrol ederim. Özellikler değiştirilirse, özel mantığımı geçerli varlık ile ilişkili olmayan diğer varlıklara uygularım ve ayrıca geçerli varlığın "UpdateAdminId" & "UpdationDate" öğesini Güncelleştir. Bunu yayınla, Centext'te "SaveChanges" yöntemini çağırıyorum.

Yukarıda bahsettiğim her şey gayet iyi çalışıyor, "SaveChanges" aramasında bir ara nokta koyup, User tarafından değiştirilen bazı alanları farklı bir değere güncellemem dışında "DbUpdateConcurrencyException" EF5 tarafından atılmaz. Örnek: & koşullu Güncelleştirmeyi, ilgimin özellikleri mükemmel bir şekilde çalışacak şekilde değiştirildiğinde, özel mantığımı tetikleyebilirim. Ancak eşzamanlılık durumunda hata alamıyorum, çünkü kayıt DB arasında kayıt alma, kayıt güncelleme ve kaydetme arasında bir kayıt güncellendiğinde EF "DbUpdateConcurrencyException" u yükseltmiyor.

Gerçek senaryoda, yeni oluşturulan kampanyayı denetleyen ve bunlar için portföy oluşturan ve IsPortfolioCreated özelliğini aşağıdaki gibi işaretleyen bir çevrimdışı cron çalışması vardır; bu süre içinde kullanıcı kampanyayı düzenleyebilir ve bayrak yanlış olarak ayarlanabilir cron portföyleri yaratmış olsa bile.

Eşzamanlılık senaryosunu çoğaltmak için SaveChanges üzerinde bir kesme noktası koyarım ve aynı varlık için MS-Sql kuruluş yöneticisinden IsPortfolioCreated feild'i güncellerim, ancak Mağazadaki Veri güncellenmiş olsa bile "DbUpdateConcurrencyException" atılmaz. .

İşte başvuru için benim kod, bu (geliştirici olarak) size kadar eşzamanlılık hakkında özel bir şey eşzamanlılık sorunlarını kontrol edecek şekilde yapılandırın yapmıyor

Public bool EditGeneralSettings(CampaignDefinition campaignDefinition) 
{ 
    var success = false; 
    //campaignDefinition.UpdatedAdminId is updated in controller by retreiving it from RquestContext, so no its not comgin from client 
    var updatedAdminId = campaignDefinition.UpdatedAdminId; 
    var updationDate = DateTime.UtcNow; 
    CmsContext context = null; 
    GlobalMasterContext globalMasterContext = null; 

    try 
    { 
     context = new CmsContext(SaveTimeout); 

     var contextCampaign = context.CampaignDefinitions.Where(x => x.CampaignId == campaignDefinition.CampaignId).First(); 

     //Always use this fields from Server, no matter what comes from client 
     campaignDefinition.CreationDate = contextCampaign.CreationDate; 
     campaignDefinition.UpdatedAdminId = contextCampaign.UpdatedAdminId; 
     campaignDefinition.UpdationDate = contextCampaign.UpdationDate; 
     campaignDefinition.AdminId = contextCampaign.AdminId; 
     campaignDefinition.AutoDecision = contextCampaign.AutoDecision; 
     campaignDefinition.CampaignCode = contextCampaign.CampaignCode; 
     campaignDefinition.IsPortfolioCreated = contextCampaign.IsPortfolioCreated; 

     var campaignNameChanged = contextCampaign.CampaignName != campaignDefinition.CampaignName; 

     // Will be used in the below if condition.... 
     var originalSkeForwardingDomain = contextCampaign.skeForwardingDomain.ToLower(); 
     var originalMgForwardingDomain = contextCampaign.mgForwardingDomain.ToLower(); 

     //This also not firing concurreny exception.... 
     var key = ((IObjectContextAdapter) context).ObjectContext.CreateEntityKey("CampaignDefinitions", campaignDefinition); 
     ((IObjectContextAdapter)context).ObjectContext.AttachTo("CampaignDefinitions", contextCampaign); 
     var updated = ((IObjectContextAdapter)context).ObjectContext.ApplyCurrentValues(key.EntitySetName, campaignDefinition); 
     ObjectStateEntry entry = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntry(updated); 
     var modifiedProperties = entry.GetModifiedProperties(); 

     //Even tried this , works fine but no Concurrency exception 
     //var entry = context.Entry(contextCampaign); 
     //entry.CurrentValues.SetValues(campaignDefinition); 
     //var modifiedProperties = entry.CurrentValues.PropertyNames.Where(propertyName => entry.Property(propertyName).IsModified).ToList(); 

     // If any fields modified then only set Updation fields 
     if (modifiedProperties.Count() > 0) 
     { 
      campaignDefinition.UpdatedAdminId = updatedAdminId; 
      campaignDefinition.UpdationDate = updationDate; 
      //entry.CurrentValues.SetValues(campaignDefinition); 
      updated = ((IObjectContextAdapter)context).ObjectContext.ApplyCurrentValues(key.EntitySetName, campaignDefinition); 

      //Also perform some custom logic in other entities... Then call save changes 

      context.SaveChanges(); 

      //If campaign name changed call a SP in different DB.. 
      if (campaignNameChanged) 
      { 
       globalMasterContext = new GlobalMasterContext(SaveTimeout); 
       globalMasterContext.Rename_CMS_Campaign(campaignDefinition.CampaignId, updatedAdminId); 
       globalMasterContext.SaveChanges(); 
      } 
     } 
     success = true; 
    } 
    catch (DbUpdateConcurrencyException ex) 
    { 
     //Code never enters here, if it does then I am planning to show the user the values from DB and ask him to retry 
     //In short Store Wins Strategy 

     //Code in this block is not complete so dont Stackies don't start commenting about this section and plague the question... 

     // Get the current entity values and the values in the database 
     var entry = ex.Entries.Single(); 
     var currentValues = entry.CurrentValues; 
     var databaseValues = entry.GetDatabaseValues(); 

     // Choose an initial set of resolved values. In this case we 
     // make the default be the values currently in the database. 
     var resolvedValues = databaseValues.Clone(); 


     // Update the original values with the database values and 
     // the current values with whatever the user choose. 

     entry.OriginalValues.SetValues(databaseValues); 
     entry.CurrentValues.SetValues(resolvedValues); 

    } 
    catch (Exception ex) 
    { 
     if (ex.InnerException != null) 
      throw ex.InnerException; 
     throw; 
    } 
    finally 
    { 
     if (context != null) context.Dispose(); 
     if (globalMasterContext != null) globalMasterContext.Dispose(); 
    } 
    return success; 
} 
+0

Varlık bazı özellikler için [ConcurrencyCheck] özelliğine sahip mi? –

+0

@ omar.ballerani no eşzamanlılık denetim özniteliği varlığa ait değil. Ama bence bu, aşağıdaki MSDN makalesinde anlatıldığı gibi çalışmalı. https://msdn.microsoft.com/en-in/data/jj592904.aspx – Vipresh

+0

deneyimim için DbUpdateConcurrencyException, güncellenecek satırı bulamadığında Entity Framework tarafından atılır. Bir veya daha fazla özellik için [ConcurrencyCheck] özniteliğini eklediğinizde, ef, veritabanı için ilk yaklaşım kullandığı –

cevap

2

Varlık çerçeve bu. Sen DbUpdateConcurrencyException, bu istisna belgelerine yakalamaya çalışıyoruz

diyor ki: DBContext tarafından atılan "İstisna bir varlık için SaveChanges veritabanında hiçbir satır etkilendi bir veritabanı güncellemesinden ama aslında neden olacağını tahmin edildiğinde. ", bir veritabanı ilk yaklaşımda o here

okuyabilir, siz (varsayılan Hiçbiri olan) 'Sabit' üzerinde sütun için mülkiyet 'eşzamanlılık Modu' belirlemek zorunda.Bu ekran görüntüsüne bakın: enter image description here

Sütun Sürümü, her satır değiştiğinde otomatik olarak güncelleştirilen özel bir tür olan SQL SERVER TIMESTAMP tipidir, bu dosyayı okuyun here. İç bağlamdan güncelleme Yukarıdaki örnekte

try 
{ 
    using (var outerContext = new testEntities()) 
    { 
     var outerCust1 = outerContext.Customer.FirstOrDefault(x => x.Id == 1); 
     outerCust1.Description += "modified by outer context"; 
     using (var innerContext = new testEntities()) 
     { 
      var innerCust1 = innerContext.Customer.FirstOrDefault(x => x.Id == 1); 
      innerCust1.Description += "modified by inner context"; 
      innerContext.SaveChanges(); 
     } 
     outerContext.SaveChanges(); 
    } 
} 
catch (DbUpdateConcurrencyException ext) 
{ 
    Console.WriteLine(ext.Message); 
} 

işlencek, dış bağlamdan güncelleştirme olacaktır: tüm beklendiği gibi çalışıp çalışmadığını Bu yapılandırmayla

, bu basit testi ile deneyebilirsiniz Bir DbUpdateConcurrencyException attı çünkü EF, filtreyi 2 sütunu kullanarak varlığı güncellemeyi deneyecek: Kimlik ve Sürüm sütunu.

Bu yardımcı olur umarız!