0

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

サービスが開始されると、内部の接続文字列を使用して、独自の構成ファイルからすべてのデータベースも接続されます。しかし、ご存じのように、接続が破損して使用できなくなったり、最後にはアクセスできなくなることさえあります。したがって、この問題を解消するために、構成内のすべての接続をチェックする特別なタイマーがあります。

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

もう 1 つ知っておくべきことは、メソッド自体で接続を維持する方法です。

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

問題は、最初のタイマーのチェックが時間内に完了できず、最初のタイマーの実行中に 2 番目のタイマーのチェックが開始される場合があることです。ヒープメモリリークで奇妙なエラーが発生します(そう思います)。もう 1 つの問題は、シングルトン デザイン パターンを開発して 1 回だけチェックインするようにしても、チェックがハングし、別のチェックが起動されない場合があることです (一部のデータベースでは、接続を開くのに非常に長い時間がかかるためです。なぜそうなるのかはわかりませんが、を追加しようとしましたConnection timeout=30;が、正直なところ、正しい結果が得られたかどうかは覚えていません。確かにそうではありません。)

次に、このサービスを時間内に実行すると、実行中と同じ量のメモリが必要になります。私はこのコードで多くの操作を試みましたが、コードのどこかでデッドロックに直面したことさえありました (ほぼ 100% の CPU 負荷)。

はい、ネイティブ ADO .NET プーリングについては知っていますが、上記の方法で機能する巨大なレガシー コード ベースがあり、既存のチェック ロジックを何らかの方法で改善する必要があるため、使用できません。

では、プーリングせずに時間内に接続を有効にするにはどうすればよいでしょうか? 可能な限りメモリの使用量を減らし (既存のコードは、実行中により多くのメモリを消費します)、わずかなコード変更で高速に動作する方法が必要です。

どんな助けでも大歓迎です!あなたの助けを心から願っていますstackoverflowコミュニティ!

最後にサイクルで接続をチェックする特別なスレッドを使用することを考えていますThread.Sleep(60000)。しかし、私はそれについてよくわかりません。

ADO .NET プロバイダーは、Devart dotConnect for Oracleです。データベースは Oracle 9-11 (サーバーによって異なります)、C# .NET Framework 4 です。

4

1 に答える 1

1
  1. このレジストリの場所 HKLM\SOFTWARE\ORACLE (たとえば、C:\oracle\client\10g) で Oracle ホームを見つけます。
  2. ORACLE_HOME の下の network\admin ディレクトリを開きます (例: C:\oracle\client\10g\network\admin)。
  3. sqlnet.ora というファイルを開く (または、まだ存在しない場合は作成する)
  4. 次の行を追加します: SQLNET.EXPIRE_TIME=1

何が起こるかというと、プローブが 1 分ごとに DB サーバーに送信され、接続が維持されます。

それが役に立てば幸い。ダニエル

于 2011-11-09T19:43:28.063 に答える