.net 在应用程序关闭之前,Firebird实际上不会删除表

.net 在应用程序关闭之前,Firebird实际上不会删除表,.net,firebird,firebird-3.0,.net,Firebird,Firebird 3.0,我有一个.net测试应用程序,它创建表,使用表,然后多次删除表 当我使用Firebird ADO.Net数据提供程序5.9.1在Firebird 3.0.2数据库上运行此应用程序时,它会在必须创建一个与先前删除的表同名的表时立即失败:表确实存在 重新启动应用程序可以避免麻烦,但我不想在每次测试后重新启动它 这是非常相似的,但是挂起了,直接使用Firebirdisql工具而不是.Net应用程序 有没有一种方法可以在不重新启动应用程序的情况下实际删除Firebird中的表 该应用程序测试许多其他数据

我有一个.net测试应用程序,它创建表,使用表,然后多次删除表

当我使用Firebird ADO.Net数据提供程序5.9.1在Firebird 3.0.2数据库上运行此应用程序时,它会在必须创建一个与先前删除的表同名的表时立即失败:表确实存在

重新启动应用程序可以避免麻烦,但我不想在每次测试后重新启动它

这是非常相似的,但是挂起了,直接使用Firebird
isql
工具而不是.Net应用程序

有没有一种方法可以在不重新启动应用程序的情况下实际删除Firebird中的表

该应用程序测试许多其他数据库,但没有这个问题:SQL Server、SQL Server Compact Edition、MySql、SQLite、Oracle、PostgreSQL

这是Firebird的MCVE失败。用具有连接字符串的适当代码替换前两行。所有其他代码只是Firebird ADO.Net数据提供程序和NUnit。它并不像我的实际应用程序那样失败,但我认为这是相同的潜在问题

[Test]
public void CreateSelectDrop()
{
    var cfg = TestConfigurationHelper.GetDefaultConfiguration();
    var cnxStr = cfg.Properties[Environment.ConnectionString];
    using (var cnx = new FbConnection(cnxStr))
    {
        cnx.Open();
        using (var tran = cnx.BeginTransaction())
        {
            using (var cmd = cnx.CreateCommand())
            {
                cmd.Transaction = tran;
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "create table test (id int not null primary key)";
                cmd.ExecuteNonQuery();
            }
            tran.Commit();
        }
    }

    using (var cnx = new FbConnection(cnxStr))
    {
        cnx.Open();
        using (var tran = cnx.BeginTransaction())
        {
            using (var cmd = cnx.CreateCommand())
            {
                cmd.Transaction = tran;
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "insert into test (id) values (1)";
                cmd.ExecuteNonQuery();
            }
            tran.Commit();
        }
    }

    using (var cnx = new FbConnection(cnxStr))
    {
        cnx.Open();
        using (var tran = cnx.BeginTransaction())
        {
            using (var cmd = cnx.CreateCommand())
            {
                cmd.Transaction = tran;
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "select id from test";
                using (var reader = cmd.ExecuteReader())
                {
                    Assert.IsTrue(reader.Read());
                    Assert.AreEqual(1, reader.GetInt32(0));
                    Assert.IsFalse(reader.Read());
                }
            }
            tran.Commit();
        }
    }

    using (var cnx = new FbConnection(cnxStr))
    {
        cnx.Open();
        using (var tran = cnx.BeginTransaction())
        {
            using (var cmd = cnx.CreateCommand())
            {
                cmd.Transaction = tran;
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "delete from test";
                cmd.ExecuteNonQuery();
            }
            tran.Commit();
        }
    }

    using (var cnx = new FbConnection(cnxStr))
    {
        cnx.Open();
        using (var tran = cnx.BeginTransaction())
        {
            using (var cmd = cnx.CreateCommand())
            {
                cmd.Transaction = tran;
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "drop table test";
                cmd.ExecuteNonQuery();
            }
            tran.Commit();
        }
    }
}
仅仅从表中选择是不够的。只有在我将删除添加到测试之后,问题才出现。它在最后一次事务提交时失败,该事务是drop中的一次,消息为:

FirebirdSql.Data.FirebirdClient.FbException : lock conflict on no wait transaction
unsuccessful metadata update
object TABLE "TEST" is in use
  ----> FirebirdSql.Data.Common.IscException : lock conflict on no wait transaction
unsuccessful metadata update
object TABLE "TEST" is in use
   at FirebirdSql.Data.FirebirdClient.FbTransaction.Commit()
   at NHibernate.Test.DialectTest.FirebirdDialectFixture.CreateSelectDrop()

根据,这个问题似乎只限于Firebird ADO.Net数据提供程序。他将其范围缩小到从2.7.7版本切换到3.0.0版本。

表删除似乎被延迟,直到使用它们的连接实际关闭,而不仅仅返回到池中。清除连接池会导致实际执行这些延迟的删除

所以在我的例子中,在删除表之前添加类似于以下代码的代码可以解决这个问题:

using (var connection = GetConnection())
{
    FbConnection.ClearPool(connection);
}
这种溶液是在5000多人中进行的一次单独试验中发现的

看起来还有另一个选项,调用
FbConnection.clearlallpool()
。虽然我没有检查它,但前者可能只清除提供的连接字符串的连接池,而后者清除所有连接池,而不管它们的连接字符串如何

由于它是一个测试应用程序,具有一些通用逻辑和一些特殊性,因此我将实际用作解决方案的代码是:

// Firebird will pool each connection created during the test and will 
// marked as used any table referenced by queries. It will delays those
// tables drop until connections are actually closed.
// This results in other tests failing when they try to create tables with
// same name.
// By clearing the connection pool the tables will get dropped. This is done
// by the following code.
// Moved from NH1908 test case, contributed by Amro El-Fakharany.
var clearConnection = Sfi.ConnectionProvider.GetConnection();
try
{
    var fbConnectionType = clearConnection.GetType();
    var clearPool = fbConnectionType.GetMethod("ClearPool");
    clearPool.Invoke(null, new object[] {clearConnection});
}
finally
{
    Sfi.ConnectionProvider.CloseConnection(clearConnection);
}

请显示一些代码,听起来好像您没有提交事务。表在事务提交时被删除,而不是在执行drop语句时被删除;根据隔离级别和其他并发事务的不同,如果表正在使用,这可能会失败或等待提交。@markrotVeel。这是NHibernate测试项目。创建和删除由其hbm2ddl工具发布。由于这是一个ORM,具体的连接和命令是由提供者提供的,带有一些抽象层,用于拥有一个不可知的API(独立于数据库供应商)。因此,在这种情况下显示代码将有点毫无意义。事实上,模式操作看起来根本不是事务性的。但似乎只有Firebird对此有问题。无论如何,如果这是一个丢失(自动?)提交案例,并且Firebird ADO.Net数据提供程序符合ADO.Net协议,那么它应该在连接返回到池后立即回滚这些事务。而不是在清除池时提交它们。我刚刚测试了在不清除池的情况下处理该工具的功能:在删除表后仍然无法用相同的名称重新创建表,这次是使用
FbException:NoWait事务上的锁冲突,不成功的元数据更新-对象表“EMPLOYEE”正在使用中
。当然,在尝试下一个创建表之前,我已经提交了drop事务。无论是否有事务,只有清除连接池才有效。但是,对于事务性创建/删除,在尝试删除表之前,还必须清除它。我使用的测试本身就是事务性的。现在我在这个问题中添加了一个MCVE。还有一个细节:Firebird已经通过NHibernate版本进行了测试,但它是通过Firebird v2.5和嵌入式模式进行测试的。现在,我正在将其迁移到Firebird v3,并使用实际的Firebird服务器。在这个设置中,它失败了,正如在这个问题中所写的。有时候FB3不会断开连接,当你要求它时,直到相当长的延迟过去。请参阅Fb3发行说明中的>>“Linger”数据库关闭SuperserverBTW,您是否尝试了其他ADO.Net提供程序-?不,我没有尝试其他提供程序。在NHibernate,我们倾向于测试默认配置。由于Firebird官方网站指向其数据提供商,因此我们使用该数据提供商进行测试。如果有证据表明它不是Firebird的主流.Net提供商,为什么不切换,但我们不知道这样的证据。由于目前没有人要求在NHibernate默认提供程序中包含另一个数据提供程序,与我们目前使用的相反,我们的用户似乎不太使用另一个。有两个提供程序,一个是FLOSS,另一个是商业提供程序。用户可以自由选择他们更喜欢的内容。你也是。但是,如果你向Jiri争辩说,他的提供商工作错误,除非它将对nHibernate测试模式实施特殊支持,那么证明其他FB提供商添加了此支持(如果他们添加了)可能是一个额外的论点。我不是来争论的,我在这里发了帖子,以防有比直接清理池更好的解决方案。对我来说,更改提供程序也不是一个选项,因为这将错过测试最常见的情况。