C# 实体框架-如何处理批量保存更改失败

C# 实体框架-如何处理批量保存更改失败,c#,entity-framework,quickbooks,C#,Entity Framework,Quickbooks,在我的C#程序中,我使用实体框架将本地SQL Server数据库与QuickBooks数据同步。从QuickBooks获取数据似乎没有任何问题。然而,在批量提交实体时,我遇到了一个绊脚石 目前,我正在使用可配置数量的实体构建DataContext,然后批量提交实体。到目前为止,批处理还没有失败,但如果失败了呢?我的想法是迭代批处理,一次提交一个实体,然后记录导致提交失败的实体 但是,我看不到一种处理数据上下文的方法,因为在使用SaveChanges()时,它似乎是一个全有或全无的问题。有没有一种

在我的C#程序中,我使用实体框架将本地SQL Server数据库与QuickBooks数据同步。从QuickBooks获取数据似乎没有任何问题。然而,在批量提交实体时,我遇到了一个绊脚石

目前,我正在使用可配置数量的实体构建
DataContext
,然后批量提交实体。到目前为止,批处理还没有失败,但如果失败了呢?我的想法是迭代批处理,一次提交一个实体,然后记录导致提交失败的实体

但是,我看不到一种处理数据上下文的方法,因为在使用
SaveChanges()
时,它似乎是一个全有或全无的问题。有没有一种方法来处理我试图完成的事情,或者我应该以一种完全不同的方式来处理失败

以下是我目前拥有的代码,以防您想查看:

 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++;
 }
int itemscont=0;
int itemsSynced=0;
int itemsFailed=0;
ArrayList异常=新建ArrayList();
int batchSliceCount=Properties.Settings.Default.SyncBatchSize//从设置中获取最大批量大小
int指数=1//用于跟踪数据上下文中当前批大小的索引
List currentBatch=新列表();//保存当前批次的列表
db=新的DataContext(DatabaseHelper.GetLocalDatabaseConnectionString());
foreach(QBResponse.customers中的var客户)
{
itemsont++;
尝试
{
string debugMsg=“使用以下详细信息保存客户…”+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);
客户newCustomer=新客户();
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(新客户);
currentBatch.Add(新客户);
}
其他的
{
//dbCustomer.CopyPropertiesFrom(customer,db);
dbCustomer.CopyCustomer(customer,db);
dbCustomer.EditedBy=Enums.OperationUser.SyncOps;
dbCustomer.EditedDateTime=DateTime.Now;
dbCustomer.SyncStatus=true;
currentBatch.Add(dbCustomer);
}
尝试
{
if(index%batchSliceCount==0 | | index==opResponse.customers.Count())//提交批处理的时间
{
UpdateStatus(Enums.LogLevel.Information,“将“+batchSliceCount+”客户的批次保存到本地数据库”);
db.SaveChanges();
itemsSynced+=currentBatch.Count();
currentBatch=新列表();
db.Dispose();
db=新的DataContext(DatabaseHelper.GetLocalDatabaseConnectionString());
}
}
捕获(例外情况除外)
{
string errorMsg=“提交批处理时出错。一次提交一个批处理。”+Environment.NewLine;
errorMsg+=“错误为:”+ex.GetBaseException().Message+Environment.NewLine+“堆栈跟踪:”+ex.GetBaseException().StackTrace;
UpdateStatus(Enums.LogLevel.Debug、errorMsg、progressPercentage);
//这里要做什么?有没有一种方法可以正确地迭代上下文并一次提交一个更改?
}
}
捕获(例外情况除外)
{
//记录异常并重新启动数据上下文
db.Dispose();
db=新的DataContext(DatabaseHelper.GetLocalDatabaseConnectionString());
}
Sleep(Properties.Settings.Default.SynchronizationSleepTimer);
索引++;
}

这取决于您要从中恢复的异常


如果您只是想寻找一种在连接中断时重试的方法,那么您可以使用一种自定义的执行策略,如果出现本文中演示的特定错误,您可以根据该策略重试。

当然,这是一个棘手的问题,下面是我要做的。。。如果SaveChanges出现异常,我会尝试将内容写入日志,以便至少更改仍然可用。然而,英孚应该负责这项权利,它的“跟踪”能力。我以前从未做过恢复方面的工作,所以我只是在这里大声思考。我知道使用执行策略来处理超时。我还没有实施它,但它已经在发展路线图上了。我更关心的是诸如密钥冲突之类的异常(即,正在提交的实体批被SQL Server拒绝)。由于重试无法解决密钥冲突错误,因此我建议您在尝试将该批安全保存到存储区之前,自己验证该批