我创建了一个用于保存Firebird数据库连接的单例类,并且我正在执行查询(见下文),这个查询就像一个魅力,直到我不得不在其中一个应用程序中添加一些定时器来设置&定期检查一些数据。
现在我经常会收到错误消息“不支持并行事务”。我无法得到它,因为我在finally块中关闭了连接。
我已经花了很长时间在网上搜索并试图弄清楚如何接近,但似乎我需要支持。
以下是我fBird
课程的主要部分:
public sealed class fBird {
private FbConnection FbConn = new FbConnection(connectionString);
static fBird m_oInstance = null;
static readonly object m_oPadLock = new object();
public static fBird Instance {
get {
lock (m_oPadLock) {
if (m_oInstance == null) {
m_oInstance = new fBird();
}
return m_oInstance;
}
}
}
//Data query:
public DataTable qSelect(string query){
if(!getState().Equals("Open")) FbConn.Open();
using (FbTransaction transaction = FbConn.BeginTransaction()) {
try{
FbCommand cmd = new FbCommand(query, FbConn, transaction);
FbDataAdapter adpt = new FbDataAdapter(cmd);
DataTable dt = new DataTable();
adpt.Fill(dt);
transaction.Commit();
return dt;
} catch (Exception ex){
transaction.Rollback();
throw new Exception("DataQuery: " + ex.Message);
} finally {
FbConn.Close();
}
}
}
//NonQuery query:
public int NonQuery(string query){
if(!getState().Equals("Open")) FbConn.Open();
using (FbTransaction transaction = FbConn.BeginTransaction()) {
try{
FbCommand cmd = new FbCommand(query, FbConn, transaction);
int i = cmd.ExecuteNonQuery();
transaction.Commit();
return i;
} catch (Exception ex){
transaction.Rollback();
throw new Exception("NonQuery: " + ex.Message);
} finally {
FbConn.Close();
}
}
}
}
以下是我如何使用该课程:
DataTable dt = fBird.Instance.qSelect("SELECT * FROM USERS");
int i = fBird.Instance.NonQuery("DELETE FROM TABLE");
以下是我使用计时器的方法:
public partial class MainForm : Form {
private System.Timers.Timer aTimer;
...
void setTimers() {
aTimer = new System.Timers.Timer(1500));
aTimer.Elapsed += OnTimedEvent;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
void OnTimedEvent(Object source, ElapsedEventArgs e) {
try{
//different changes to & reads from database
} catch(Exception ex) {
MessageBox.Show("OnTimedEvent: " + ex.Message);
}
}
}
我发现的是,由于某种原因,Firebird的连接不止一个(单身不应该允许,对吧?)。我可以在网络选项卡上的Windows性能监视器中看到它。作为测试我做了非常不同的操作,打开不同的窗口和放大器。应用程序中的屏幕,也保存一些更改。随机我得到错误(并行事务)并创建新连接,并且一段时间(如30-40次点击)一切正常。
这让我疯狂。我希望你们中的某个人能够帮助我。我觉得我在这里做了一些非常错的事,但我找不到它。如果您需要更多信息,请与我们联系。
答案 0 :(得分:1)
如果您使用多个线程,那么您应该不共享一个连接。您的问题不是多个连接之一,而是多个线程使用相同的连接,并尝试创建自己的事务,驱动程序明确不支持。您的代码也可能受到其他竞争条件的影响。
停止使用该单身人士。而是为每个工作单元获取一个新连接,并在完成后关闭它。默认情况下,Firebird ADO.net提供程序提供连接池,因此它相对便宜。
就个人而言,我会完全摆脱那个fBird
类,但至少要摆脱共享实例,实现IDisposable
来关闭dispose上的连接,添加一个静态方法来创建实例一经请求。然后,您应该在fBird
中使用这些using
个实例,以确保它在最后得到正确关闭。
我建议摆脱那个fBird
类的原因是,你只是包装了普通的连接对象,并提供了更糟糕和更脆弱的抽象。
答案 1 :(得分:0)
您需要使用锁定,单身人士不需要单个用户
答案 2 :(得分:0)
您的fBird课程有三件事可能需要重新考虑。
首先,正如其他答案所指出的那样,FBConnection对象不是线程安全的。 (这是Firebird的设计/限制,而不是C#库特定的行为)。这意味着您可以:
其次,您的方法非常繁重,因为您每次都在进行完全连接/断开连接。当你调高音量时,这会让事情变慢。
第三,你总是在NonQuery中提交。如果您的操作涉及多个查询,那么如果两者之间出现故障,您的数据库可能会保持不一致。
这是一个想法,作为前两点的妥协解决方案。在我的一个旧应用程序(不是c#sorry)中,我创建了一个连接代理来减少restful服务中的连接/断开开销。这具有最大并发连接配置值。
您编写了GetConnection和ReturnConnection方法。
GetConnection内部锁定,同时确定现有连接是否可用。然后它将其标记为正在使用并返回它。如果没有可用的连接,则会建立一个新连接(延迟创建),将其标记为正在使用并返回它(假设您处于最大限制之下)。
ReturnConnection内部锁定然后将其标记为可用。如果连接可疑,则返回状态时有一个可选参数(例如,使用时出现套接字错误)。 ReturnConnection做的另一件事是给它加时间戳。如果一段时间没有使用,后台进程会定期关闭超过某个计数的连接。
GetConnection保证事务未启动。如果ReturnConnection在事务中(调用者必须提交,回滚或指示存在问题)或连接已关闭,则抛出异常。