2017-05-30 2 views
1

In meinem C# -Programm verwende ich Entity Framework, um eine lokale SQL Server-Datenbank mit QuickBooks-Daten zu synchronisieren. Das Abrufen der Daten von QuickBooks scheint keine Probleme zu haben. Allerdings stoße ich bei Batch-Commits von Entitäten auf einen Stolperstein.Entity Framework - Behandlung von Batch-SaveChange-Fehlern

Momentan baue ich die DataContext mit einer konfigurierbaren Anzahl von Entitäten und dann die Entitäten in Batch fest. Bisher ist die Charge nicht ausgefallen, aber was passiert, wenn sie es tut? Meine Idee, dies zu bekämpfen, wäre es, über den Batch zu iterieren und jede Entity nacheinander zu senden und dann die einen oder die anderen zu protokollieren, die den Commit-Fehler verursachen.

Allerdings sehe ich keinen Weg, dies mit dem Datenkontext zu tun, da es scheint, ein Alles oder nichts zu sein, wenn verwendet wird. Gibt es einen Weg, mit dem fertig zu werden, was ich zu erreichen versuche, oder sollte ich mich mit den Fehlern auf eine ganz andere Weise befassen? Hier

ist der Code, den ich zur Zeit haben, falls Sie einen Blick an ihm nehmen wollen:

int itemsCount = 0; 
int itemsSynced = 0; 
int itemsFailed = 0; 

ArrayList exceptions = new ArrayList(); 

int batchSliceCount = Properties.Settings.Default.SyncBatchSize; //Getting the max batch size from the settings 
int index = 1; //Index used for keeping track of current batch size on data context 
List<Customer> currentBatch = new List<Customer>(); // List to hold curent batch 

db = new DataContext(DatabaseHelper.GetLocalDatabaseConnectionString()); 

foreach (var customer in QBResponse.customers) 
{ 
    itemsCount++; 

    try 
    { 
     string debugMsg = "Saving Customer with the Following Details....." + Environment.NewLine; 
     debugMsg += "ListId: " + customer.CustomerListId + Environment.NewLine; 
     debugMsg += "FullName: " + customer.FullName + Environment.NewLine; 
     int progressPercentage = (itemsCount * 100)/opResponse.retCount; 
     UpdateStatus(Enums.LogLevel.Debug, debugMsg, progressPercentage); 

     var dbCustomer = db.Customers.FirstOrDefault(x => x.CustomerListId == customer.CustomerListId); 

     if (dbCustomer == null) 
     { 
      // customer.CopyPropertiesFrom(customer, db); 
      Customer newCustomer = new Customer(); 
      newCustomer.CopyCustomer(customer, db); 
      newCustomer.AddBy = Enums.OperationUser.SyncOps; 
      newCustomer.AddDateTime = DateTime.Now; 
      newCustomer.EditedBy = Enums.OperationUser.SyncOps; 
      newCustomer.EditedDateTime = DateTime.Now; 
      newCustomer.SyncStatus = true; 

      db.Customers.Add(newCustomer); 
      currentBatch.Add(newCustomer); 
     } 
     else 
     { 
      //dbCustomer.CopyPropertiesFrom(customer, db); 
      dbCustomer.CopyCustomer(customer, db); 
      dbCustomer.EditedBy = Enums.OperationUser.SyncOps; 
      dbCustomer.EditedDateTime = DateTime.Now; 
      dbCustomer.SyncStatus = true; 
      currentBatch.Add(dbCustomer); 
     } 

     try 
     { 
      if (index % batchSliceCount == 0 || index == opResponse.customers.Count()) //Time to submit the batch 
      { 
       UpdateStatus(Enums.LogLevel.Information, "Saving Batch of " + batchSliceCount + "Customers to Local Database"); 
       db.SaveChanges(); 
       itemsSynced += currentBatch.Count(); 
       currentBatch = new List<Customer>(); 
       db.Dispose(); 
       db = new DataContext(DatabaseHelper.GetLocalDatabaseConnectionString()); 
      } 
     } 
     catch (Exception ex) 
     { 
      string errorMsg = "Error occured submitting batch. Itterating and submitting one at a time. " + Environment.NewLine; 
      errorMsg += "Error Was: " + ex.GetBaseException().Message + Environment.NewLine + "Stack Trace: " + ex.GetBaseException().StackTrace; 
      UpdateStatus(Enums.LogLevel.Debug, errorMsg, progressPercentage); 

      //What to do here? Is there a way to properly iterate over the context and submit a change one at a time? 
     } 
    } 
    catch (Exception ex) 
    { 
     //Log exception and restart the data context 
     db.Dispose(); 
     db = new DataContext(DatabaseHelper.GetLocalDatabaseConnectionString()); 
    } 

    Thread.Sleep(Properties.Settings.Default.SynchronizationSleepTimer); 
    index++; 
} 
+0

Schwierige Frage sicher, hier ist, was ich tun würde ... Wenn es eine Ausnahme auf den SaveChanges gab, würde ich versuchen, den Inhalt in ein Protokoll zu schreiben, so dass zumindest die Änderungen noch verfügbar waren. Allerdings sollte EF dieses Recht mit seiner "Tracking" -Fähigkeit übernehmen. Ich musste noch nie mit Genesung arbeiten, also denke ich hier nur laut nach. –

Antwort

0

es auf die Ausnahmen ab, die Sie von erholen wollen ...

Wenn Sie suchen nur nach einer Möglichkeit, es erneut zu versuchen, wenn die Verbindung unterbrochen wurde, können Sie eine benutzerdefinierte Ausführungsstrategie basierend auf DbExecutionStrategy verwenden, die wiederholt, wenn bestimmte Fehler auftreten, wie in diesem Artikel CodeProject gezeigt.

+0

Ich bin mir bewusst, eine Ausführungsstrategie zu verwenden, um mit Timeouts umzugehen. Ich muss es noch implementieren, aber es ist auf der Entwicklungs-Roadmap. Ich mache mir mehr Sorgen über Ausnahmen wie Schlüsselverletzungen (I. E. Der Entitätsstapel, der festgeschrieben wird, wird von SQL Server abgelehnt). – Stephen

+0

Da es keine Möglichkeit gibt, Wiederholungsversuche zu vermeiden, würde ich empfehlen, die Charge selbst zu validieren, bevor Sie versuchen, sie im Geschäft zu speichern – user1859022