BS架构的MVC项目可以直接使用EntityFrameWork的形式建立数据库连接,连接信息会记录在config文件中,但是对于Winfrom项目,可以自己搭建数据库连接信息

string dataBaseConnect="Data Source=244db;user=dc_ch;password=dc;Pooling = True;Max Pool Size = 1024;Min Pool Size = 4;";

当执行的类型是存储过程时,默认是同步执行,对于一些比较耗时的任务,可以设置成异步执行的方式,只需要在连接中声明 AsynchronousProcessing = TRUE;

在正文开始之前先了解一下数据库连接池的概念

一.什么是数据库连接池

每当程序需要读写数据库的时候。Connection.Open()会使用ConnectionString连接到数据库,数据库会为程序建立 一个连接,并且保持打开状态,此后程序就可以使用T-SQL语句来查询/更新数据库。当执行到Connection.Close()后,数据库就会关闭当 前的连接。

但是我在实际操作过程中,如果频繁的开启/关闭连接,对资源无疑是一种浪费,ADO.Net Team就给出了一个比较好地解决方法。将先前的Connection保存起来,当下一次需要打开连接的时候就将先前的Connection 交给下一个连接。这就是Connection Pool。

二、数据库Connection Pool连接池的运行方式

1、当一个程序执行Connection.open()时候,ADO.net就需要判断,此连接是否支持Connection Pool (Pooling 默认为True)

(1)、如果指定为False, ADO.net就与数据库之间创建一个连接,然后返回给程序。

(2)、如果指定为 True,ADO.net就会根据ConnectString创建一个Connection Pool,然后向Connection Pool中填充Connection。填充多少个Connection由Min Pool Size (默认为0)属性来决定。例如如果指定为5,则ADO.net会一次与SQL数据库之间打开5个连接,然后将4个Connection,保存在 Connection Pool中,1个Connection返回给程序。

2、当程序执行到Connection.close() 的时候。如果Pooling 为True,ADO.net 就把当前的Connection放到Connection Pool并且保持与数据库之间的连接。

同时还会判断Connection Lifetime(默认为0)属性,0代表无限大,如果Connection存在的时间超过了Connection LifeTime,ADO.net就会关闭的Connection同时断开与数据库的连接,而不是重新保存到Connection Pool中。

3、当下一次Connection.Open() 执行的时候,ADO.Net就会判断新的ConnectionString与之前保存在Connection Pool中的Connection的connectionString是否一致。

4、 ADO.net需要判断当前的Connection Pool中是否有可以使用的Connection(没有被其他程序所占用),如果没有的话,ADO.net就需要判断ConnectionString设 置的Max Pool Size (默认为100)

(1)、如果Connection Pool中的所有Connection没有达到Max Pool Size,ADO.net则会再次连接数据库,创建一个连接,然后将Connection返回给程序。

(2)、如果已经达到了 MaxPoolSize,ADO.net就不会再次创建任何新的连接,而是等待Connection Pool中被其他程序所占用的Connection释放,这个等待时间受SqlConnection.ConnectionTimeout(默认是15 秒)限制,也就是说如果时间超过了15秒,SqlConnection就会抛出超时错误。

5、如果有可用的Connection,从Connection Pool 取出的Connection也不是直接就返回给程序,ADO.net还需要检查ConnectionString的ConnectionReset属性 (默认为True)是否需要对Connection 最一次reset。

三、ASP.NET程序对max pool size的配置

<add key="data" value="server=192.168.1.123; Port=3306; uid=root; pwd=root;database=data;pooling=true;min pool size=5;max pool size=512;connect timeout = 20; "/> 

 其中Max Pool Size如果未设置则默认为100,理论最大值为32767。最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。在等待队列中,默认等待与服务器的连接的时间为15秒。

四、查看应用程序池占用数量

select * from sysprocesses where dbid= db_id('数据库名')

五、max pool size的配置相关 常见的错误

异常详细信息

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

问题描述

我们获取连接超过连接池最大值时产生如上异常。通常连接池最大值为100。当我们获取连接超过最大值时,ADO.NET等待连接池返回连接而超时,这样将抛出如上异常

解决方法         

1、要做的是在我们使用连接后立即关闭连接

2、在访问数据库的页面上使用数据缓存,如果页面的数据不是经常更新(几分钟更新一次)的话,使用Cache对象可以不用访问数据库而使用缓存中的内容,那么可以大大减少连接数量。           

3、可以在WEB.config 里面修改连接字符串中的Max Pool Size = N;来动态扩大连接池中的连接最大数量。

首先声明一个公共连接方法类,这个类里面声明了建立连接方法和事务相关(开启,提交,回滚)

public class TransController : IDisposable
    {
        #region 成员变量
        //数据库类型
        DatabaseType _DatabaseType = DatabaseType.ORACLE;

        /// <summary>
        /// 数据库连接
        /// </summary>
        IDbConnection _cnn = null;

        /// <summary>
        /// 内部事务
        /// </summary>
        IDbTransaction _trans = null;

        /// <summary>
        /// 内部Helper
        /// </summary>
        IHelper _helper = null;

        /// <summary>
        /// ADOHelper
        /// </summary>
        ADOHelper _ADOHelper = null;

        /// <summary>
        /// 事务状态
        /// </summary>
        TransState _state = TransState.NotInTrans;

        /// <summary>
        /// 事务是否启动标志
        /// </summary>
        bool _isTransfer = false;

        /// <summary>
        ///事务状态枚举
        /// </summary>
        enum TransState
        {
            InTrans = 0,
            NotInTrans = 1
        }
        #endregion

        #region 成员属性
        /// <summary>
        /// 事务
        /// </summary>
        public IDbTransaction Trans
        {
            get { return _trans; }
        }

        /// <summary>
        /// Helper
        /// </summary>
        public IHelper Helper
        {
            get { return _helper; }
        }
        /// <summary>
        /// ADOHelper
        /// </summary>
        public ADOHelper ADOHelper
        {
            get { return _ADOHelper; }
        }

        /// <summary>
        /// 是否启用事务
        /// </summary>
        public bool IsTransfer
        {
            get { return _isTransfer; }
        }

        /// <summary>
        /// 连接数据库类型
        /// </summary>
        public DatabaseType DatabaseType
        {
            get { return _DatabaseType; }
        }
        #endregion

        #region 过时接口,保持向下兼容
        /// <summary>
        /// 数据库类型,已过时,保持向下兼容
        /// </summary>
        public string dbTypeNew
        {
            get { return _DatabaseType.ToString(); }
        }
        #endregion

        #region 构造和析构
        internal TransController(IHelper helper, ADOHelper adohelper, string cnnString)
        {
            _cnn = helper.CreateConnection(cnnString);
            _cnn.Open();
            _helper = helper;
            _ADOHelper = adohelper;
            _DatabaseType = helper.DatabaseType;
        }

        /// <summary>
        /// 析构函数,释放资源
        /// </summary>
        ~TransController()
        {
            Dispose(false);
        }
        #endregion

        #region 事务控制接口
        /// <summary>
        /// 启动事务
        /// </summary>
        public void BeginTransaction()
        {
            if (_isTransfer)
            {
                return;
            }

            if (_state == TransState.NotInTrans)
            {
                _trans = _cnn.BeginTransaction();
                _state = TransState.InTrans;
            }
        }

        /// <summary>
        /// 提交事务
        /// </summary>
        public void Commit()
        {
            if (_isTransfer)
            {
                return;
            }

            Debug.Assert(_state == TransState.InTrans && _trans != null);

            if (_state == TransState.InTrans)
            {
                _trans.Commit();
                _state = TransState.NotInTrans;
            }
        }

        /// <summary>
        /// 回滚事务
        /// </summary>
        public void Rollback()
        {
            if (_isTransfer)
            {
                return;
            }

            Debug.Assert(_state == TransState.InTrans && _trans != null);

            if (_state == TransState.InTrans)
            {
                _trans.Rollback();
                _state = TransState.NotInTrans;
            }
        }

        /// <summary>
        /// 开始内部事务
        /// </summary>
        public void StartTransfer()
        {
            Debug.Assert(_isTransfer == false && _state == TransState.InTrans && _trans != null);
            _isTransfer = true;
        }

        /// <summary>
        /// 结束内部事务
        /// </summary>
        public void EndTransfer()
        {
            Debug.Assert(_isTransfer == true && _state == TransState.InTrans && _trans != null);
            _isTransfer = false;
        }

        /// <summary>
        /// 关闭
        /// </summary>
        public void Close()
        {
            Dispose();
        }
        #endregion

        #region IDisposable 成员
        /// <summary>
        /// 释放占用资源
        /// </summary>
        public void Dispose()
        {
            if (_isTransfer)
            {
                EndTransfer();
                return;
            }

            Dispose(true);

            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            _cnn = null;
        }
        #endregion
    }

然后实际处理过程中使用该类建立数据库连接

string dataBaseType="ORACLE";
string dataBaseConnect="Data Source=244db;user=dc_ch;password=dc;Pooling = True;Max Pool Size = 1024;Min Pool Size = 4;";
TransController link = HelperFactory.CreateTransController(modual.DataBase.Type, modual.DataBase.Connect);

        /// <summary>
		/// 根据指定的数据库类型创建数据库操作对象
		/// </summary>
		/// <param name="type">数据库类型</param>
		/// <returns>Helper接口</returns>
		public static Helper CreateHelper(DatabaseType type)
		{
			switch (type)
			{
				case DatabaseType.SQLSERVER:
				{
					return new SqlHelper();
				}
				case DatabaseType.ORACLE:
				{
					return new OracleHelper();
				}
				default:
				{
					throw new Exception(DONT_SUPPORT);
				}
			}
		}

		#region 创建事务控制器
		/// <summary>
		/// 创建事务控制器
		/// </summary>
		/// <param name="dbType">数据库类型</param>
		/// <param name="cnnString">连接字符串</param>
		/// <returns>事务控制器</returns>
		public static TransController CreateTransController(DatabaseType dbType, string cnnString)
		{
			Helper helper = CreateHelper(dbType);

			return new TransController(helper, new ADOHelper(helper), cnnString);
		}

这样,这个link我们就可以直接使用了。举个例子

//缓存功能参数
            try
            {
                if (this.link != null)
                {
                    log.Info("加载接口缓存数据.");
                    FuncCache.Modual = modual.ModualCode;
                    this.link.BeginTransaction();
                    DataSet item = this.link.Helper.ExecuteDataset(this.link.Trans, CommandType.Text, "SELECT * FROM SYS_FUNCTIONINFO WHERE FUNCTIONSTATE = 'N' ORDER BY FUNCTIONCODE");
                    if (item != null || item.Tables[0].Rows.Count > 0)
                    {
                        FuncCache.FuncInfo = item.Tables[0];
                    }
                    item = this.link.Helper.ExecuteDataset(this.link.Trans, CommandType.Text, "SELECT T.* FROM SYS_FUNCTIONDETAIL T,SYS_FUNCTIONINFO F WHERE T.FUNCTIONCODE = F.FUNCTIONCODE AND F.FUNCTIONSTATE = 'N' ORDER BY T.FUNCTIONCODE,T.POSITION");
                    if (item != null || item.Tables[0].Rows.Count > 0)
                    {
                        FuncCache.FuncParam = item.Tables[0];
                    }
                    this.link.Commit();
                }
            }
            catch (Exception ex)
            {
                this.link.Rollback();
                RetMsg = ex.Message;
                throw new Exception("获取功能接口 " + ex.Message);
            }
//其中CommandType指明执行的是SQL或存储过程
    public enum CommandType
    {
        Text = 1,
        StoredProcedure = 4,
        TableDirect = 512
    }

这里就是winform下如何管理数据库连接相关的知识了。