事务是否总是提交(确保文件只导入一次)?

时间:2013-09-04 12:39:19

标签: c# sql sql-server transactions

我提出的问题需要一些介绍。我有一个应用程序,每5分钟从一个文件夹导入小文件和电子邮件到自己的SQL Server数据库。

非常重要
  1. 必须导入每个文件
  2. 每个文件只能导入一次
  3. 现在第1部分并不是什么大问题。然而,第2部分引起了一些担忧。如果在成功导入后无法删除该文件(例如由于缺少访问权限),则下次我的应用程序查看该文件夹时,将一次又一次地导入该文件,直到某人修复了访问权限问题。

    因此,我想知道以下方法是否可以安全地工作,而不会丢失数据:

    1. 开始交易
    2. 将文件导入数据库
    3. 从文件夹中删除文件
    4. 提交事务(如果出现错误则回滚)
    5. 这就是我到达问题标题的地方:如果之前的步骤没有引发异常,那么最后一次提交步骤是否会成功?我可以安全地在提交之前删除文件,没有丢失文件的风险?如果SQL Server在事务期间关闭怎么办?或者,如果文件删除失败,我是否应该从数据库中删除记录?

3 个答案:

答案 0 :(得分:1)

您所说的“交易”仅用于确保数据完全提交到数据库。要添加持久性以确保不能多次导入文件,您有多个选项,但我们考虑以下内容:

  1. 导入文件时,将文件名添加到一个唯一约束的表中,以便SQL INSERT失败,从而回滚整个“事务”。这当然只有在文件名实际上是唯一的时才有效。如果文件名不唯一,那么看看你是否能找到足够的信息。通常情况下,您可以添加其他域信息(例如发票#,客户#等)以使其唯一,因此INSERT仍然失败。
  2. 保留已处理的所有文件的列表,不再处理它们。您可以使用XML文件甚至内存来执行此操作,但它仍然存在风险,因为如果用户在其中删除了具有相同名称的新文件 - 那么您实际上想要导入那个。我认为选项1 是最好的选择。
  3. 您需要意识到的一件事是,您正在管理两种不同的技术。删除文件,或者我应该说你的尝试,与数据库事务没有任何关系,除非你制作它。所以,假设你有这样的流程:

    using (SqlConnection c ...)
    using (SqlCommand cmd ...)
    {
        SqlTransaction t = c.BeginTransaction();
        try
        {
            // update database
    
            // try to delete file
    
            t.Commit();
        }
        catch (Exception ex)
        {
            // you know something failed
            // you can catch more specific exceptions and respond here
        }
    }
    

    如果未达到t.Commit(),则数据库将不会更新。

答案 1 :(得分:0)

通常我会先将文件存档到另一个位置。然后我会建立一个存储导入元数据的地方,这将存储文件名和文件的日期以及导入的成功或失败。然后你可以证明每个文件都已导入,并且仍然有任何失败的副本。

然后我会在事务中运行该进程以导入文件。我会在启动导入事务之前将有关该文件的元数据放入数据库中(因此它赢了;在失败时回滚)然后在提交之后,我会向元数据添加信息以让我知道文件已经成功进口。当然作为数据人,我会在SSIS中完成所有这些工作,我可以使用本机日志记录来帮助我记录每个步骤(有助于查看故障发生的位置)。导入完成后,我会从处理位置删除该文件,但我会在存档位置保留一段时间。 (后来有一些alawys问题,为什么数据看起来很像它有助于手头有实际文件向他们展示他们以这种方式发送给你的信息!)

答案 2 :(得分:0)

假设您使用的是相对较新版本的Windows(Vista +)且正在使用NTFS,您可以在事务中登记文件操作。

我不相信.NET库内置了这个内容,但是有一个第三方库here可以让你做这样的事情:

// Wrap a file copy and a database insert in the same transaction
TxFileManager fileMgr = new TxFileManager();
using (TransactionScope scope1 = new TransactionScope())
{
    // Delete the file
    fileMgr.DeleteFile(fileName);

    // Do some database stuff
    dbMgr.ExecuteNonQuery(insertSql);

    scope1.Complete();
}