在 MS 访问文件上使用 dispose 和 SuppressFinalize 后文件已在使用中的异常

Posted

技术标签:

【中文标题】在 MS 访问文件上使用 dispose 和 SuppressFinalize 后文件已在使用中的异常【英文标题】:file already in use exception after using dispose and SuppressFinalize on MS access File 【发布时间】:2016-04-07 15:35:20 【问题描述】:

我正在开发通过 GUI 对数据库进行更改的软件。我想在用户单击保存后压缩数据库。保存后用户可以继续使用软件或关闭,所以我没有使用“使用”。我创建了 databaseAccess 对象,它与其他几个对象一起保存 OleDbConnection 连接对象。这是我的数据库访问类。

using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;

namespace TreeTool

    public class DataBaseAccess
    
        #region Properties
        private string m_directory;
        public List<string> selectedTableNames;
        private Dictionary<String, DataTable> selectedTables;
        private OleDbConnection mdbConnection;
        DataTable dataTable;

        //Constructor
        public DataBaseAccess()
        
            selectedTableNames = new List<string>();
            selectedTables = new Dictionary<string, DataTable>();
        

        public string directory
        
            get
            
                return m_directory;
            
            set
            
                m_directory = value;
            
        
        #endregion
        public List<string> GetAllTableNames()
        
            if (dataTable != null)
            
                List<string> tableList = new List<string>();
                for (int i = 0; i < dataTable.Rows.Count; i++)
                
                    string TableName = dataTable.Rows[i][2].ToString();
                    tableList.Add(TableName);
                
                return tableList;
            
            return null;
        

        /// <summary>
        /// Returns Table Columns
        /// </summary>
        /// <returns></returns>
        public DataTable GetTable(string TableName)
        
            DataTable mdbTable;
            if (selectedTables.TryGetValue(TableName, out mdbTable))
            
                return mdbTable;
            
            else
            
                mdbTable = new DataTable();
                //mdbConnection.Open();
                string mdbCommandString = "SELECT * FROM [" + TableName + "]";
                OleDbDataAdapter QueryCommand = new OleDbDataAdapter(mdbCommandString, mdbConnection);
                QueryCommand.Fill(mdbTable);
                //mdbConnection.Close();

                selectedTables.Add(TableName, mdbTable);

                return mdbTable;
            
        

        public void SetTable(String TableName, DataTable dataTable)
        
            //mdbConnection.Open();

            OleDbCommand ac = new OleDbCommand("delete from [" + TableName + "]", mdbConnection);
            ac.ExecuteNonQuery();

            foreach (DataRow row in dataTable.Rows)
            
                String query = "INSERT INTO [" + TableName + "] (TaskID, htmlTopic, nRelative, [Group], nKey,"
                    + " [nText], nImage, nSelImage, nFontName, nFontInfo, Keywords) VALUES (@TaskID,"
                    + " @HTMLTopic, @nRelative, @Group, @nKey, @nText, @nImage, @nSelImage, @nFontName, "
                    + " @nFontInfo, @Keywords)";
                OleDbCommand command = new OleDbCommand(query, mdbConnection);
                command.Parameters.AddWithValue("@TaskID", row["TaskID"]);
                command.Parameters.AddWithValue("@HTMLTopic", row["HTMLTopic"]);
                command.Parameters.AddWithValue("@nRelative", row["nRelative"]);
                command.Parameters.AddWithValue("@Group", row["Group"]);
                command.Parameters.AddWithValue("@nKey", row["nKey"]);
                command.Parameters.AddWithValue("@nText", row["nText"]);
                command.Parameters.AddWithValue("@nImage", row["nImage"]);
                command.Parameters.AddWithValue("@nSelImage", row["nSelImage"]);
                command.Parameters.AddWithValue("@nFontName", row["nFontName"]);
                command.Parameters.AddWithValue("@nFontInfo", row["nFontInfo"]);
                command.Parameters.AddWithValue("@Keywords", row["Keywords"]);
                command.ExecuteNonQuery();
            

            //mdbConnection.Close();
        

        internal bool validTable(string TableName)
        
            DataTable mdbTable = new DataTable();

            //mdbConnection.Open();

            string mdbCommandString = "SELECT * FROM [" + TableName + "]";
            OleDbDataAdapter QueryCommand = new OleDbDataAdapter(mdbCommandString, mdbConnection);
            QueryCommand.Fill(mdbTable);
            //mdbConnection.Close();

            // check if table contains all columns necessary
            String[] columnNames = new string[]  "TaskID" , "HTMLTopic", "nRelative", "Group", "nKey",
                "nText", "nImage", "nSelImage", "nFontName", "nFontInfo", "Keywords";

            Boolean missingColumn = false;

            DataColumnCollection columns = mdbTable.Columns;
            foreach (String columnName in columnNames)
            
                if (columns.Contains(columnName) == false)
                
                    // print the message
                    MessageBox.Show("Database: " + directory + " Table: " + TableName + " is missing column \"" + columnName
                        + "\". Add it to make changes.",
                        "Missing column",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Exclamation,
                        MessageBoxDefaultButton.Button1);
                    missingColumn = true;
                
            

            if (missingColumn == true)
            
                return false;
            
            return true;
        

        public void insertTable(String tableName)
        
            selectedTableNames.Add(tableName);
        

        public List<String> getSelectedTables()
        
            return selectedTableNames;
        

        public Boolean isConnected()
        
            if (mdbConnection == null)
            
                return false;
            
            return true;
        

        public void connect()
        
            if (mdbConnection == null)
            
                String m_mdbDirectory = @"Provider=Microsoft.JET.OLEDB.4.0;Data Source=" + m_directory;
                mdbConnection = new OleDbConnection(m_mdbDirectory);
                mdbConnection.Open();
                string[] restrictions = new string[4];
                restrictions[3] = "Table";
                dataTable = mdbConnection.GetSchema("TABLES", restrictions);
                //mdbConnection.Close();
            
        

        public void disconnect()
        
            mdbConnection.Close();
            mdbConnection.Dispose();
            GC.SuppressFinalize(mdbConnection);
            mdbConnection = null;
        

        public void clearSelectedTables()
        
            selectedTableNames.Clear();
        
    

保存和压缩功能是这样的

    private void save()
       
        foreach(DataBaseAccess database in databases)
        
            // save changes code
            database.disconnect();
            CompactAndRepairAccessDB(database.directory);
            database.connect();
        
    

    private void CompactAndRepairAccessDB(string accessFile)
    
        string tempFile = @"temp.mdb";
        FileInfo temp = new FileInfo(tempFile);

        // Required COM reference for project:
        // Microsoft Office 14.0 Access Database Engine Object Library
        var dbe = new Microsoft.Office.Interop.Access.Dao.DBEngine();
        try
        
            dbe.CompactDatabase(accessFile, tempFile);
            temp.CopyTo(accessFile, true);
            temp.Delete();
        
        catch (Exception e)
        
            Console.WriteLine("Error: " + e.Message);
        
    

异常发生在“dbe.CompactDatabase(accessFile, tempFile);”行。

【问题讨论】:

错误发生在代码的哪一部分? dbe.CompactDatabase(accessFile, tempFile); 你为什么使用GC.SuppressFinalize?为什么不希望调用 Finalizer? 为什么connect() 调用mdbConnection.Close();disconnect() 呢?在 connect 函数中将 mdbConnection.Close(); 注释掉后,对我来说一切正常。 你确定不是 temp.mdb 被锁定了吗? - 可能值得使用 System.IO.Path.GetTempFileName() 来消除它。也可能值得在调用 CompactDatabase 之前显式处理所有 DataTable、OleDbDataAdapter 和 OleDbCommand 对象(或清除所有引用并显式调用 System.GC.Collect()),以查看这是否会改变行为。 【参考方案1】:

在您使用OleDbDataAdapterOleDbCommand 的所有方法的代码中,请确保对这些对象使用 using-Pattern。即使实际连接已经释放,他们也可以保持 mdb 文件打开。

需要修改的方法好像是GetTableSetTableValidTable

【讨论】:

在 OleDbDataAdapter 和 OleDbCommand 上使用“使用”有效。谢谢。

以上是关于在 MS 访问文件上使用 dispose 和 SuppressFinalize 后文件已在使用中的异常的主要内容,如果未能解决你的问题,请参考以下文章

如何在Linux上安装和使用MS SQL Server

在 dispose 方法中访问 BlocProvider.of<Bloc>(context)

装好IIS组件后,路径也对,创建了一个文件,却无法访问???

Linux零碎知识

SQL 和 MS 访问 - 过滤表单上的数据

为啥在 BinaryReader 上调用 Dispose() 会导致编译错误?