C# 停止SQL Server/EF执行自动回滚
因此,我使用EF6执行一些原始SQL查询,我希望在事务中使用它,因为我正在根据查询结果移动文件。以下是代码的简短版本:C# 停止SQL Server/EF执行自动回滚,c#,sql,entity-framework,C#,Sql,Entity Framework,因此,我使用EF6执行一些原始SQL查询,我希望在事务中使用它,因为我正在根据查询结果移动文件。以下是代码的简短版本: using (var dbTransaction = customDb.Database.BeginTransaction()) { //Drop previously created tmp tables... customDb.Database.ExecuteSqlCommand("IF OBJECT_ID('dbo.bbldkmember_tmp', 'U
using (var dbTransaction = customDb.Database.BeginTransaction()) {
//Drop previously created tmp tables...
customDb.Database.ExecuteSqlCommand("IF OBJECT_ID('dbo.bbldkmember_tmp', 'U') IS NOT NULL DROP TABLE dbo.bbldkmember_tmp");
//Create a tmp table to keep all the bulk inserted records
customDb.Database.ExecuteSqlCommand("CREATE TABLE bbldkmember_tmp ( " + TableCreateGuts + " )");
//Insert all record from the files into the TMP table, move the files to the archive when done, error if the file fails.
foreach (var fileName in filenamesToHandle) {
try {
customDb.Database.ExecuteSqlCommand(
@"BULK INSERT bbldkmember_tmp FROM
'" + fileName + @"'
WITH (
FIELDTERMINATOR = ';',
ROWTERMINATOR = '\n',
CODEPAGE = 'ACP',
FIRSTROW = 2
);");
//Move file
File.Move(fileName, fileName.Insert(fileName.LastIndexOf(@"\"), @"\Archive"));
} catch (Exception err){
//return "Failed in file " + fileName;
//ignore but log error
retval += "Failed to load file " + fileName + " with error " + err.Message + "<br />";
File.Move(fileName, fileName.Insert(fileName.LastIndexOf(@"\"), @"\Error"));
}
}
//Start doing stuff with the records that we succeeded reading.
//Add a column that we can format a GUID into
customDb.Database.ExecuteSqlCommand(@"ALTER TABLE bbldkmember_tmp ADD col47 VARCHAR(255)");
//commit or rollback based on the results of a lot of checks
}
使用(var dbTransaction=customDb.Database.BeginTransaction()){
//删除以前创建的tmp表。。。
customDb.Database.ExecuteSqlCommand(“如果对象ID('dbo.bbldkmember\U tmp','U')不是NULL,则删除表dbo.bbldkmember\U tmp”);
//创建一个tmp表以保留所有批量插入的记录
customDb.Database.ExecuteSqlCommand(“创建表bbldkmember_tmp(“+TableCreateGuts+”);
//将文件中的所有记录插入TMP表,完成后将文件移动到存档,如果文件失败,则出错。
foreach(filenamesToHandle中的var文件名){
试一试{
customDb.Database.ExecuteSqlCommand(
@“批量插入bbldkmember\u tmp从
“+fileName+@”
与(
字段终止符=';',
行终止符='\n',
代码页='ACP',
第一行=2
);");
//移动文件
File.Move(fileName,fileName.Insert(fileName.LastIndexOf(@“\”),@“\Archive”);
}捕获(异常错误){
//返回“文件中失败”+文件名;
//忽略但记录错误
retval+=“无法加载文件”+fileName+,错误为“+err.Message+”
”;
File.Move(fileName,fileName.Insert(fileName.LastIndexOf(@“\”),@“\Error”);
}
}
//开始用我们成功读取的记录做一些事情。
//添加一列,我们可以将GUID格式化为
customDb.Database.ExecuteSqlCommand(@“ALTER TABLE bbldkmember_tmp ADD col47 VARCHAR(255)”;
//根据大量检查的结果提交或回滚
}
问题是在循环之后,EF对事务进行了回滚。ALTER TABLE失败,因为如果其中一个大容量插入失败,该列已经在表中
我要做的是读取所有文件,忽略其中有错误的文件,然后处理内容,如果出现故障,则执行回滚。这可能吗
编辑:这是一个SQL server profiler的屏幕截图,显示了正在发生的事情,显然有些事情我不明白。。。
编辑#3:所以,我检查了当文件中没有错误时会发生什么。如您所见,在大容量插入之后没有启动第二个事务。
为了进一步尝试和解决问题,我尝试了使用SQLServerTryCatch和SqlQuery。结果是相同的:
//Insert all record from the files into the TMP table, move the files to the archive when done, error if the file fails.
foreach (var fileName in filenamesToHandle)
{
try
{
var c = customDb.Database.SqlQuery<int>(
@"--Bulk insert:
SET NOCOUNT ON;
BEGIN TRY
BULK INSERT bbldkmember_tmp FROM
'" + fileName + @"'
WITH (
FIELDTERMINATOR = ';',
ROWTERMINATOR = '\n',
CODEPAGE = 'ACP',
FIRSTROW = 2
);
SELECT 1
END TRY
BEGIN CATCH
SELECT 0
END CATCH
").ToList();
//Move file
retval += "Loaded file " + fileName + " with message " + (c.First() == 1 ?"ok":"fail") + "<br />";
if(File.Exists(fileName.Insert(fileName.LastIndexOf(@"\"), c.First() == 1 ? @"\Archive": @"\Error")))
File.Delete(fileName.Insert(fileName.LastIndexOf(@"\"), c.First() == 1 ? @"\Archive" : @"\Error"));
File.Move(fileName, fileName.Insert(fileName.LastIndexOf(@"\"), c.First() == 1 ? @"\Archive": @"\Error"));
}
catch (Exception err)
{
//Critical error
return "Failed to load file " + fileName + " with error " + err.Message + "<br />";
}
}
//将文件中的所有记录插入TMP表,完成后将文件移动到存档,如果文件失败,则出错。
foreach(filenamesToHandle中的var文件名)
{
尝试
{
var c=customDb.Database.SqlQuery(
@“--批量插入:
不计数;
开始尝试
批量插入bbldkmember\u tmp从
“+fileName+@”
与(
字段终止符=';',
行终止符='\n',
代码页='ACP',
第一行=2
);
选择1
结束尝试
开始捕捉
选择0
端接
)。ToList();
//移动文件
retval++=“加载的文件”+文件名+”,并显示消息“+(c.First()==1?”“确定”:“失败”)+“
”;
如果(File.Exists(fileName.Insert(fileName.LastIndexOf(@“\”),c.First()==1?@“\Archive”:@“\Error”))
File.Delete(fileName.Insert(fileName.LastIndexOf(@“\”),c.First()==1?@“\Archive”:@“\Error”);
File.Move(fileName,fileName.Insert(fileName.LastIndexOf(@“\”),c.First()==1?@“\Archive”:@“\Error”);
}
捕获(异常错误)
{
//临界误差
返回“加载文件失败”+fileName+,错误为“+err.Message+”
”;
}
}
SQL Server已中止该事务。EF没有参与。您可以尝试使用try…CATCH
。我不确定这是否会阻止事务中止。(您链接到了一个不适用于此处的问题,因为它是关于XACT\u ABORT
)
一个潜在的解决方案是在主事务之外进行批量插入 您需要执行一个
dbTransaction.Commit()在离开之前,请使用块执行。如果要处理一堆文件,该怎么办。所有“工作”,但最后一个抛出一个错误。您是否回滚整个事务?现在,您的归档文件中有不在数据库中的文件。这是预期的行为吗?是的,我正在根据更新是否有效执行commit()或rollback()。如果大容量插入失败,那么我的代码就出了问题,我将不得不重做所有文件,所以是的,这是预期的行为。编辑了更多的代码以显示它是如何失败的。我们在这里没有显示的代码中找不到错误。发布提交代码。您确定EF中止了传输吗?我对此表示怀疑。SQL Server有时也会这样做以响应错误(是的,这是可怕的遗留行为)catch(Exception err){dbTransaction.Rollback();}实际上,由于TRY-catch,EF隐藏了正确的错误消息。“ALTER TABLE”语句应失败,错误为“当前事务无法提交,并且不支持写入日志文件的操作”,但EF会自动启动新事务。我应该得到这个错误的原因在关于XACT_ABORT()的帖子中解释过,这就是SQLServer“隐藏”消息。当EF开始发挥作用时,它就消失了。至少