如何使用 Jet OLEDB 打开 CSV 或 XLS 并获得表锁?

发布于 2024-08-08 11:18:20 字数 1171 浏览 3 评论 0原文

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

以下代码将打开 CSV 作为数据库并将其加载到 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 只能选择和插入]
  • 在数据库打开时锁定 XLS 文件。 (不能让多个线程和/或进程执行彼此的更改...)
  • 读入 DataTable 对象(为了便于工作)

I am trying to figure out how to read/write lock a CSV or XLS file when I read it as a Database via Jet OLEDB.

The following code will open a CSV as a DB and load it into a DataTable object:

        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; }
        }

What I want to make sure of is that while I have the CSV or XLS file open, that I have a Read/Write LOCK on the Table (aka. the file), so that any other application that comes along and tries to read/write to this file has to wait its turn.

Does this happen automatically? If not, what do I need to do to make sure this does happen?

Btw, I'm working in C#/.NET 2.0, if that makes any difference...

Update: So, I'm clarifying my requirements now:

  • XLS file (because I need SELECT and UPDATE functionality) [CSV can only SELECT and INSERT]
  • LOCK the XLS file while the DB is Open. (can't have multiple threads and/or processes stepping on each other's changes...)
  • Read into DataTable object (for ease of working)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

忆悲凉 2024-08-15 11:18:20

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

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

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

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

无论如何,这里有一个代码示例,其中显示:

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

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

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);
            });

        }
    }
}

OLEDB's Jet driver locks flat files while there's an open OleDbDataReader to them. To verify this, look at the VerifyFileLockedByOleDB method in the code sample below. Note that having an open OleDbConnection is not enough-- you have to have an open Reader.

That said, your code posted above does not keep an open connection, since it uses OleDbDataAdapter.Fill() to quickly connect to the data source, suck out all the data, and then disconnect. The reader is never left open. The file is only locked for the (short) time that Fill() is running.

Furthermore, even if you open the reader yourself and pass it into DataTable.Load(), that method will close your DataReader for you once it's done, meaning that the file gets unlocked.

So if you really want to keep the file locked and still use a DataTable, you'll need to manually populate the datatable (schema and rows!) from an IDataReader, instead of relying on DataAdapter.Fill() or DataTable.Load().

Anyway, here's a code sample which shows:

  • your original code
  • an example which won't work because DataTable.Load() will close the DataReader and unlock the file
  • an alternate approach which will keep the file locked while you're working with the data, via operating at the row level using DataReader rather than using a DataTable

UPDATE: looks like keeping a DataReader open will prevent the same process from opening the file, but another process (e.g. Excel) can open (and write to!) the file. Go figure. Anyway, at this point I'd suggest, if you really want to keep the file locked, consider using something else besides OLEDB where you have more fine-grained control over how (adn when!) the file is opened and closed. I'd suggest the CSV reader fromhttp://www.codeproject.com/KB/database/CsvReader.aspx, which is well-tested and fast, but will give you the source code so if you need to change file-locking/opening/closing, you can do so.

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);
            });

        }
    }
}
2024-08-15 11:18:20

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

经过更多挖掘,我找到了此页面:

ADO 提供程序属性和设置

它说:

Jet OLEDB:数据库锁定模式

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

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 文件作为数据库打开时,这实际上似乎不会执行任何表/行/页锁定。

UPDATE: The following does not appear to lock my DB as I had hoped...

After much more digging, I found this page:

ADO Provider Properties and Settings

It says:

Jet OLEDB:Database Locking Mode

A Long value (read/write) that
specifies the mode used when locking
the database to read or modify
records.

The Jet OLEDB:Database Locking Mode
property can be set to any of the
following values:

Page-level Locking 0

Row-level Locking 1

Note A database can only be open in
one mode at a time. The first user to
open the database determines the
locking mode to be used while the
database is open.

So I assume that my code would get changed to:

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

Which should give me Page-level locking. If I wanted Row-level locking, I'd switch the value to 1.

Unfortunately, this doesn't actually appear to do any table/row/page locking when opening a CSV file as a DB.

瑶笙 2024-08-15 11:18:20

好的,所以您编写的新函数有点有效,但我仍然会遇到“竞争条件”,然后导致抛出异常。因此,在这部分代码中:

        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
                }
            }
        }

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

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

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

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

当读者完成阅读时似乎会发生异常。 (在读取最后一行之后,但仍然处于“using (OleDbDataReader reader = cmd.ExecuteReader())”循环中。

Ok, so that new function you wrote kinda works, but I still end up with a "Race condition" which then causes an exception to be thrown. So in this section of the code:

        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
                }
            }
        }

I put a breakpoint on the line with the comment "breakpoint here" and then ran the program. I then located the CSV file in File Explorer and tried to open it with Excel. It causes Excel to wait for the file to be unlocked, which is good.

But here's the bad part. When I clear the breakpoint and then tell it to continue debugging, Excel sneaks in, grabs a lock on the file and causes an exception in my running code.

(The exception is: The Microsoft Jet database engine cannot open the file ''. It is already opened exclusively by another user, or you need permission to view its data.)

I guess I can always wrap that code in a try-catch block, but when the exception occurs, I won't know if it is a legitimate exception or one caused by this weird condition.

The exception seems to occur when the Reader is finished reading. (after it reads the last row, but still is in the "using (OleDbDataReader reader = cmd.ExecuteReader())" loop.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文