在没有本机池的情况下保持有效的DB连接

时间:2011-11-09 17:07:26

标签: c# .net oracle connection-pooling devart

有没有办法让DB连接保持打开并准备在运行时使用? 我们的WCF服务应该以这种方式保持连接。 现在我们的代码看起来类似于这段代码:

// This is the class of objects that has connections within.
[DataContract]
public class SomeObj
{
    [DataMember]
    public string Name { get; set; }

    public OracleConnection Conn { get; set; }
}

// Dictionary of SomeObjs
private static Dictionary<string, SomeObj> myObjs = new Dictionary<string, SomeObj>();

当服务启动时,它还连接自己的配置文件中的所有数据库和内部的连接字符串。但是,如您所知,连接可能会损坏并无法使用,甚至最终无法访问。因此,为了消除这个问题,我们有一个特殊的计时器来检查config中的所有连接:

    // Setup timer method. Timer will fire every minute.
    static private void CreateTimer()
    {
        Timer Timer1 = new System.Timers.Timer();
        Timer1.Enabled = true;
        Timer1.Interval = 60000;
        Timer1.Elapsed += new System.Timers.ElapsedEventHandler(Timer1_Elapsed);
    }

    static private void Timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
            Parallel.ForEach(Program.config.Root.Elements("databases"), el =>
            {
                try
                {
                    Program.OpenConnection(el.Attribute("name").Value);
                }
                catch (Exception exc)
                {
                    Console.WriteLine(DateTime.Now.ToString() + " " + exc);                        
                }
            });

            // This is old way we checks connections consistently
            //foreach (XElement element in Program.config.Root.Elements("databases"))
            //{
            //    try
            //    {                
            //        AServ.OpenConnection(el.Attribute("name").Value);                
            //    }
            //    catch (Exception exc)
            //    {
            //        Console.WriteLine(DateTime.Now.ToString() + " " + exc);                
            //    }
            //}                
    }


    public static bool OpenConnection(string name)
    {
        // if connections exactly doesn't exists
        if (!myObjs.ContainsKey(name))
            {
                XElement el = (from t in config.Root.Elements("databases")
                               where t.Attribute("name").Value == name
                               select t).FirstOrDefault();
                if (el == null)
                {
                    lock (myObjs)
                    {
                        myObjs.Remove(name);                            
                    }
                    return false;
                }
                lock (myObjs)
                {
                    myObjs.Add(name, new SomeObj { Conn = new OracleConnection { ConnectionString = el.Attribute("connectionString").Value } }, Name = el.Attribute("name").Value);
                }
            }
            // if connection broken
            if ((myObjs[name].Conn.State != ConnectionState.Open || !myObjs[name].Conn.Ping()) && myObjs.ContainsKey(name))
            {
                try
                {
                    // Trying to get it alive 
                    lock (myObjs)
                    {
                        myObjs[name].Conn.Close();
                        myObjs[name].Conn.Open();
                    }
                }
                catch (Exception e)
                {
                    if (!myObjs.ContainsKey(name))
                        return false;

                    lock (myObjs)
                    {
                        myObjs.Remove(name);
                    }

                    return false;                        
                }
                Console.WriteLine(DateTime.Now.ToString() + " " + myObjs[name].Conn.ConnectionString);                    
            }

        return true;
    }

还有一件事你应该知道,伙计们,我们是如何使用方法本身保持连接的:

    private static int GetParameterValue(string name, int id)
    {
        // if we don't get parameter value at the moment because broken connection
        if (!OpenConnection(name))
            return -1;

        // assuming connection is OK and getting the parameter value
        string parameterQuery = @"select GetInfo(:id) from dual";
        OracleCommand parameterCommand = new OracleCommand(parameterQuery, myObjs[name].Conn);
        parameterCommand.Parameters.Add("id", id);            
        return Convert.ToInt32(parameterCommand.ExecuteScalar());
    }

问题是有时第一次计时器检查无法及时完成,第二次计时器检查在第一次运行时开始。它给堆内存泄漏带来了一些奇怪的错误(我想是这样)。另一个问题是即使我们开发单一设计模式只能进行一次检查,检查可能会挂起而另一次检查永远不会被解雇(因为打开连接需要花费很长时间才能完成某些数据库。我不知道不知道为什么会发生这种情况,但是我们试图添加Connection timeout=30;,但说实话,我记不起它给出了正确的结果。当然不是。)。

然后这个服务及时运行它需要的内存与运行时一样多。我已尝试过使用此代码进行多次操作,甚至在代码中遇到死锁一次(几乎100%CPU负载)。

是的,我知道本机ADO .NET池,但是我们不能使用它,因为我们有巨大的遗留代码库,以上面说明的方式工作,并且应该以某种方式改进现有的检查逻辑。

那么请告诉我你的想法如何在没有合并的情况下及时获得连接? 我需要一些可以使用尽可能少的内存的方法(现有代码在运行时占用更多内存),并且在代码更改时可以快速工作。

非常感谢任何帮助!非常希望得到您的帮助stackoverflow社区!

我考虑使用特殊线程来检查最后与Thread.Sleep(60000)的循环中的连接。但我不确定。

ADO .NET提供程序是Devart dotConnect for Oracle。数据库是Oracle 9-11(取决于服务器),C#.NET Framework 4。

1 个答案:

答案 0 :(得分:1)

  1. 在此注册表位置找到您的oracle主页HKLM \ SOFTWARE \ ORACLE(例如C:\ oracle \ client \ 10g)
  2. 打开ORACLE_HOME下的network \ admin目录(例如C:\ oracle \ client \ 10g \ network \ admin)
  3. 打开(或创建,如果它尚不存在)名为sqlnet.ora
  4. 的文件
  5. 添加以下行:  SQLNET.EXPIRE_TIME = 1
  6. 将会发生探测,每1分钟发送一次探测到数据库服务器,这将使连接保持活动状态。

    希望它有所帮助。 丹尼尔