数据库
首页 > 数据库> > [C#]记录一次异常排查,关于using语法、sqlserver数据库session、DBHelper类

[C#]记录一次异常排查,关于using语法、sqlserver数据库session、DBHelper类

作者:互联网

最近在做一个基于asp.net和sqlserver的网站项目,发现网站运行一段时间之后,会报异常:

超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小

这异常明显是sqlserver数据库连接池超出了默认大小,估计是代码哪里忘了释放DB链接了。

排查数据访问层代码跟DBHelper,搞了半天解决了。

总结一点东西,记录一下。

 

DBHelperA代码:

public  class DBHelperA
    {
        public string connStr = "你的sqlserver数据库连接";

        public IDbConnection _conn;

        public IDbConnection Conn
        {
            get
            {
                if (_conn == null)
                {
                    _conn = new SqlConnection(connStr);
                }
                return _conn;
            }
        }
    }

ClassA代码:

 public class ClassA :DBHelperA
    {
        public void DoItFirst()
        {
            using (Conn)
            {
                if (Conn.State == ConnectionState.Closed)
                {
                    Conn.Open();
                    Console.WriteLine(Conn.GetHashCode());
                }
            }
        }

        public void DoItSecond()
        {
            using (Conn)
            {
                if (Conn.State == ConnectionState.Closed)
                {
                    Conn.Open();
                    Console.WriteLine(Conn.GetHashCode());
                }
            }
        }
    }

创建ClassA对象并调用两个方法:

public static void TestA()
        {
            for (int i = 1; i < 200; i++)
            {
                ClassA a = new ClassA();
                a.DoItFirst(); 
                a.DoItSecond();
                Console.WriteLine(i);
            }
            Console.ReadLine();
        }

以上代码乍一看没啥问题,但是一运行,会在 a.DoItSecond()方法抛出异常:ConnectionString属性未初始化

 

 

这是因为,在执行完DoItFirst()方法之后,using语句释放了Conn对象占用的资源,但是Conn对象并不为空

所以在执行DoItSecond()方法时,并不会重新创建Conn对象。导致还用那个已经释放了资源的Conn对象去打开数据库连接,导致异常的发生

 

DBHelperB代码:

public  class DBHelperB
    {
        public string connStr = "你的sqlserver数据库连接";

        public IDbConnection Conn
        {
            get
            {
                var conn = new SqlConnection(connStr);
                Console.WriteLine(conn.GetHashCode());
                return conn;
            }
        }
    }

ClassB代码:

public class ClassB :DBHelperB
    {
        public void DoIt()
        {
            using (Conn)
            {
                if (Conn.State == ConnectionState.Closed)
                {
                    Conn.Open();
                    Console.WriteLine(Conn.GetHashCode());
                }
            }
        }
    }

创建ClassB对象并调用方法:

public static void TestB()
        {
            for (int i = 1; i < 200; i++)
            {
                ClassB b = new ClassB();
                b.DoIt();
                Console.WriteLine(i);
            }
            Console.ReadLine();
        }

执行抛出异常:

 

 

 

 

此时查看数据库的session连接,发现有100个连接:

 

 

这是因为DoIt方法中,每次对Conn对象的引用,都会返回一个新的连接,所以using语句打开和释放的不是同一个conn对象,导致数据库中创建了很多连接

  

 

DBHelperC代码:

public  class DBHelperC
    {
        public string connStr = "你的sqlserver数据库连接";
    }

 

 

 

 

 ClassC代码:

public void DoIt()
        {
            using (var Conn = new SqlConnection(connStr))
            {
                if (Conn.State == ConnectionState.Closed)
                {
                    Conn.Open();
                    Console.WriteLine(Conn.GetHashCode());
                }
            }
        }

 

创建ClassC对象并调用方法:

 public static void TestC()
        {
            for (int i = 1; i < 200; i++)
            {
                ClassC c = new ClassC();
                c.DoIt();
                Console.WriteLine(i);
            }
            Console.ReadLine();
        }

 

执行代码,不会报任何异常。且数据库有且只有一个session

 

 

因为每次是在using语句块儿里面创建connection对象,using创建和释放的是同一个对象。

另外因为数据库的connectionString没有指定pooling参数,所以默认是启用连接池的。

所以c#代码中connection对象的创建与销毁不会影响sqlserver数据库中的对象。

每次代码创建connection对象,调用open()方法,数据库用的还都是连接池中第一次构建的那个session

 

现在我把连接字符串的pooling改成false

第一次循环时,数据库session创建时间

 

 

第一次循环跳出using语句之后,session没了

 

 

 

 第二次循环,又重新创建了新的db session

 

 

 综上所述:

在构造DBHelper类和数据库访问层的时候,一定要注意connection对象的释放。否则就会导致数据库中一堆的无效session

 

代码在这儿,有兴趣的可以试一下( 把数据库连接改成你自己的sqlserver数据库地址就可以了):

代码下载

标签:代码,Console,C#,数据库,sqlserver,DBHelper,using,public,Conn
来源: https://www.cnblogs.com/qfl-blog/p/11647129.html