【问题标题】:How to Open a CSV or XLS with Jet OLEDB and attain Table Lock?如何使用 Jet OLEDB 打开 CSV 或 XLS 并获得 Table Lock?
【发布时间】:2009-10-16 14:15:18
【问题描述】:

当我通过 Jet OLEDB 将 CSV 或 XLS 文件作为数据库读取时,我试图弄清楚如何读取/写入锁定它。

以下代码将打开 CSV 作为 DB 并将其加载到 DataTable 对象中:

        private DataTable OpenCSVasDB(string fullFileName)
        {
           string file = Path.GetFileName(fullFileName);
           string dir = Path.GetDirectoryName(fullFileName);
           string cStr = "Provider=Microsoft.Jet.OLEDB.4.0;"
               + "Data Source=\"" + dir + "\\\";"
               + "Extended Properties=\"text;HDR=Yes;FMT=Delimited\";";
           string sqlStr = "SELECT * FROM [" + file + "]";
           OleDbDataAdapter da;
           DataTable dt = new DataTable();
           try
           {
               da = new OleDbDataAdapter(sqlStr, cStr);
               da.Fill(dt);
           }
           catch { dt = null; }
        }

我要确保的是,当我打开 CSV 或 XLS 文件时,我的表(又名文件)上有一个读/写锁,以便任何其他应用程序出现并尝试要读取/写入此文件必须等待轮到它。

这会自动发生吗?如果没有,我需要做什么来确保这确实发生?

顺便说一句,我正在使用 C#/.NET 2.0,如果这有什么不同...

更新:所以,我现在澄清我的要求:

  • XLS 文件(因为我需要 SELECT 和 UPDATE 功能)[CSV 只能 SELECT 和 INSERT]
  • 在 DB 打开时锁定 XLS 文件。 (不能有多个线程和/或进程踩着彼此的更改...)
  • 读入 DataTable 对象(为了方便工作)

【问题讨论】:

    标签: c# csv locking oledb


    【解决方案1】:

    OLEDB 的 Jet 驱动程序会在打开的 OleDbDataReader 时锁定平面文件。要验证这一点,请查看下面代码示例中的 VerifyFileLockedByOleDB 方法。请注意,只有一个开放的 OleDbConnection 是不够的——您必须有一个开放的阅读器。

    也就是说,您上面发布的代码并没有保持打开的连接,因为它使用OleDbDataAdapter.Fill() 快速连接到数据源,吸出所有数据,然后断开连接。阅读器永远不会打开。该文件仅在Fill() 运行的(短)时间内被锁定。

    此外,即使您自己打开阅读器并将其传递给DataTable.Load(),该方法也会在完成后为您关闭 DataReader,这意味着文件被解锁。

    因此,如果您确实想保持文件锁定并仍然使用 DataTable,则需要从 IDataReader 手动填充数据表(模式和行!),而不是依赖 DataAdapter.Fill() 或 DataTable。加载()。

    无论如何,这是一个代码示例,它显示:

    • 您的原始代码
    • 一个不起作用的示例,因为 DataTable.Load() 将关闭 DataReader 并解锁文件
    • 另一种方法,通过使用 DataReader 而不是使用 DataTable 在行级别操作,在您处理数据时保持文件锁定

    更新: 看起来保持 DataReader 打开会阻止同一个进程打开文件,但另一个进程(例如 Excel)可以打开(并写入!)文件。去搞清楚。无论如何,在这一点上,我建议,如果您真的想保持文件锁定,请考虑使用除 OLEDB 之外的其他东西,您可以更精细地控制文件的打开和关闭方式(以及何时!)。我建议来自http://www.codeproject.com/KB/database/CsvReader.aspx 的 CSV 阅读器,它经过充分测试且速度很快,但会为您提供源代码,因此如果您需要更改文件锁定/打开/关闭,您可以这样做。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data.OleDb;
    using System.Data;
    using System.IO;
    
    namespace TextFileLocking
    {
        class Program
        {
            private static DataTable OpenCSVasDB(string fullFileName)
            {
                string file = Path.GetFileName(fullFileName);
                string dir = Path.GetDirectoryName(fullFileName);
                string cStr = "Provider=Microsoft.Jet.OLEDB.4.0;"
                    + "Data Source=\"" + dir + "\\\";"
                    + "Extended Properties=\"text;HDR=Yes;FMT=Delimited\";";
                string sqlStr = "SELECT * FROM [" + file + "]";
                OleDbDataAdapter da;
                DataTable dt = new DataTable();
                try
                {
                    da = new OleDbDataAdapter(sqlStr, cStr);
                    da.Fill(dt);
                }
                catch { dt = null; }
    
                return dt;
            }
            private static DataTable OpenCSVasDBWithLockWontWork(string fullFileName, out OleDbDataReader reader)
            {
                string file = Path.GetFileName(fullFileName);
                string dir = Path.GetDirectoryName(fullFileName);
                string cStr = "Provider=Microsoft.Jet.OLEDB.4.0;"
                    + "Data Source=\"" + dir + "\\\";"
                    + "Extended Properties=\"text;HDR=Yes;FMT=Delimited\";";
                string sqlStr = "SELECT * FROM [" + file + "]";
                OleDbConnection openConnection = new OleDbConnection(cStr);
                reader = null;
                DataTable dt = new DataTable();
                try
                {
                    openConnection.Open();
                    OleDbCommand cmd = new OleDbCommand(sqlStr, openConnection);
                    reader = cmd.ExecuteReader();
                    dt.Load (reader);       // this will close the reader and unlock the file!
                    return dt;  
                }
                catch 
                { 
                    return null; 
                }
            }
            private static void OpenCSVasDBWithLock(string fullFileName, Action<IDataReader> dataRowProcessor)
            {
                string file = Path.GetFileName(fullFileName);
                string dir = Path.GetDirectoryName(fullFileName);
                string cStr = "Provider=Microsoft.Jet.OLEDB.4.0;"
                    + "Data Source=\"" + dir + "\\\";"
                    + "Extended Properties=\"text;HDR=Yes;FMT=Delimited\";";
                string sqlStr = "SELECT * FROM [" + file + "]";
                using (OleDbConnection conn = new OleDbConnection(cStr))
                {
                    OleDbCommand cmd = new OleDbCommand(sqlStr, conn);
                    conn.Open();
                    using (OleDbDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            dataRowProcessor(reader);
                        }
                    }
                }
            }
            private static void VerifyFileLockedByOleDB(string fullFileName)
            {
                string file = Path.GetFileName(fullFileName);
                string dir = Path.GetDirectoryName(fullFileName);
                string cStr = "Provider=Microsoft.Jet.OLEDB.4.0;"
                    + "Data Source=\"" + dir + "\\\";"
                    + "Extended Properties=\"text;HDR=Yes;FMT=Delimited\";";
                string sqlStr = "SELECT * FROM [" + file + "]";
                using (OleDbConnection conn = new OleDbConnection(cStr))
                {
                    OleDbCommand cmd = new OleDbCommand(sqlStr, conn);
                    conn.Open();
                    using (OleDbDataReader reader = cmd.ExecuteReader())
                    {
                        File.OpenRead(fullFileName);   // should throw an exception
    
                        while (reader.Read())
                        {
                            File.OpenRead(fullFileName);   // should throw an exception
    
                            StringBuilder b = new StringBuilder();
                            for (int i = 0; i < reader.FieldCount; i++)
                            {
                                b.Append(reader.GetValue(i));
                                b.Append(",");
                            }
                            string line = b.ToString().Substring(0, b.Length - 1);
                            Console.WriteLine(line);
                        }
                    }
                }
            }
    
            static void Main(string[] args)
            {
                string filename = Directory.GetCurrentDirectory() + "\\SomeText.CSV";
                try
                {
                    VerifyFileLockedByOleDB(filename);
                }
                catch { }   // ignore exception due to locked file
    
                OpenCSVasDBWithLock(filename, delegate(IDataReader row)
                {
                    StringBuilder b = new StringBuilder();
                    for (int i = 0; i <row.FieldCount; i++)
                    {
                        b.Append(row[i].ToString());
                        b.Append(",");
                    }
                    string line = b.ToString().Substring(0, b.Length - 1);
                    Console.WriteLine(line);
                });
    
            }
        }
    }
    

    【讨论】:

    • 好的,所以 OleDbDataAdapter.Fill() 在一个动作中打开、锁定、填充、释放锁定和关闭。我以前不明白,但现在明白了。如何将该数据放入 DataTable 对象中? (顺便说一句,我发现 CSV 文件不适合我的项目,因为我还需要更新行,不幸的是,CSV 文件只能选择和插入。)
    • 您好 Ranger - 我扩展了上面的答案以涵盖您关于如何获得 DataTable 的问题,该 DataTable 在您处理数据的全部时间内仍会锁定连接。
    • 好的,我稍后会看看这个并回复你。感谢您的帮助!
    • 好的,我尝试将您的 OpenCSVasDBWithLock() 方法粘贴到我的代码中,它会打开/填充 DataTable,但它肯定不会锁定文件。我能够启动我的应用程序的第二个副本并打开同一个文件。同样,我能够进入目录并通过 notepad.exe 读取文件——我停止调试我的应用程序的第一个实例,它声称 OleDbConnection 仍处于打开状态。我不知道该怎么做...
    • 嗯,你确实发现了一个奇怪的地方。我现在正在更新我的答案。大约 5 分钟后见上图。
    【解决方案2】:

    更新:以下似乎没有像我希望的那样锁定我的数据库......

    经过更多的挖掘,我找到了这个页面:

    ADO Provider Properties and Settings

    上面写着:

    Jet OLEDB:数据库锁定模式

    一个 Long 值(读/写) 指定锁定时使用的模式 要读取或修改的数据库 记录。

    Jet OLEDB:数据库锁定模式 属性可以设置为任何 以下值:

    页面级锁定0

    行级锁定1

    注意数据库只能在 一次一种模式。第一个用户 打开数据库确定 锁定模式,而使用 数据库已打开。

    所以我假设我的代码会更改为:

           string cStr = "Provider=Microsoft.Jet.OLEDB.4.0;"
               + "Data Source=\"" + dir + "\\\";"
               + "Extended Properties=\"text;HDR=Yes;FMT=Delimited\";"
               + "Jet OLEDB:Database Locking Mode=0";
    

    这应该给我页面级锁定。如果我想要行级锁定,我会将值切换为 1。

    不幸的是,当将 CSV 文件作为数据库打开时,这实际上似乎不会执行任何表/行/页锁定。

    【讨论】:

      【解决方案3】:

      好的,所以您编写的新函数有点工作,但我仍然会得到一个“竞争条件”,然后导致抛出异常。所以在这一段代码中:

              using (OleDbConnection conn = new OleDbConnection(cStr))
              {
                  OleDbCommand cmd = new OleDbCommand(sqlStr, conn);
                  conn.Open();
                  using (OleDbDataReader reader = cmd.ExecuteReader())
                  {
                      while (reader.Read())
                      {
                          reader.GetString(0);  // breakpoint here
                      }
                  }
              }
      

      我在带有“断点”注释的行上放置了一个断点,然后运行程序。然后我在文件资源管理器中找到了 CSV 文件,并尝试使用 Excel 打开它。它会导致 Excel 等待文件被解锁,这很好。

      但这是不好的部分。当我清除断点并告诉它继续调试时,Excel 会潜入其中,锁定文件并在我的运行代码中导致异常。

      (例外情况是:Microsoft Jet 数据库引擎无法打开文件''。它已被其他用户以独占方式打开,或者您需要权限才能查看其数据。)

      我想我总是可以将该代码包装在一个 try-catch 块中,但是当异常发生时,我不知道它是一个合法的异常还是由这种奇怪的情况引起的。

      Reader 读完后似乎出现异常。 (在它读取最后一行之后,但仍然处于“使用(OleDbDataReader reader = cmd.ExecuteReader())”循环中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-20
        • 1970-01-01
        • 2017-10-24
        • 1970-01-01
        相关资源
        最近更新 更多