C# 链接mysql 基本操作的多线程实现

先来看看如何链接mysql,需要用到mysql.Data.dll 这个dll不是.net自带的,自行搜索下载。

MySqlConnection conn = new MySqlConnection(connectionString);//使用MySqlConnection类来链接mysql;

connectionString 的格式是 server=mysqlhost;port=mysqlport;user=mysqlusername;password=mysqlpassword;database=databasename;

链接问题解决了,接下来分析数据库操作行为
数据库操作行为无非就是两种(我只用到两种,应该还有别的),第一种是执行sql语句,第二种是执行存储过程。
先来看看执行sql语句
执行sql语句又分为查询类和非查询类,查询类需要返回结果,非查询类不需要返回结果
先来看看非查询类的

string sqlstr = "insert into testtable values('a',1)";
MySqlCommand cmd = new MySqlCommand(sql, conn);//conn是数据库链接
cmd.CommandType = CommandType.Text; //说明执行的是sql语句
if (cmd.Connection.State == ConnectionState.Closed) //如果连接已关闭就打开连接
        cmd.Connection.Open();
cmd.ExecuteNonQuery();  //去执行sql语句

接着在来看看查询类的

string sqlstr = "select * from testtable";
MySqlCommand cmd = new MySqlCommand(sql, conn);//conn是数据库链接
cmd.CommandType = CommandType.Text; //说明执行的是sql语句
if (cmd.Connection.State == ConnectionState.Closed) //如果连接已关闭就打开连接
        cmd.Connection.Open();
MySqlDataAdapter sda = new MySqlDataAdapter();
DataSet ds = new DataSet();
sda.SelectCommand = cmd;
sda.Fill(ds);   
sda.Dispose();	//一定要关闭,不然下次执行报错;

查询到的所有数据库表在 DataSet。
注意查询类与非查询的执行的区别
非查询类调用 MySqlCommand.ExecuteNonQuery()。执行sql语句
查询类调用 MySqlDataAdapter.Fill(DataSet)。执行Sql语句

再来看看执行存储过程

MySqlCommand cmd = new MySqlCommand(procedure, conn);//procedure 是存储过程名字 conn是数据库链接
cmd.CommandType = System.Data.CommandType.StoredProcedure; //设置存储过程模式
//添加存储过程参数
MySqlParameter param = cmd.Parameters.Add(paramName,type); //paramName 是参数名,type是参数类型 MySqlDbType枚举类型
param.Value = value;	//设置参数值, 注意不能传递 Null,如果是null值需要使用 DBNull.Value传递
param.Direction = pd;	//参数种类 ParameterDirection枚举,表示是输入参数、输出参数、输入输出参数。默认是输入参数。
if (cmd.Connection.State == ConnectionState.Closed) //如果连接已关闭就打开连接
        cmd.Connection.Open();
//执行存储过程
//如果需要使用到存储过程中Select 的值,
//也就是上面所说的查询类就使用MySqlDataAdapter.FiIl()执行。
//如果非查询类使用MySqlCommand.ExecuteNonQuery() 执行.
cmd.ExecuteNonQuery();

存储过程的输出参数值使用cmd.Parameters[“参数名”].Value 来获取,

现在数据库的操作都解决了。再分析下如何在多线程中使用数据库。
只要每个线程调用数据库时不使用共同的资源就不会出问题。
也就是说同一时刻不能两个线程持有同一个链接。
用一个连接池来管理链接即可解决此问题。

下面是我的完整代码:

先是对查询键做一层包装

 /// <summary>
	/// 存储过程查询键
	/// </summary>
	public class SqlProdureParameter {
	/*
        public SqlCommand sqlcommand {
            get;
            private set;
        }
*/
        public MySqlCommand mysqlcommand {
            get;
            private set;
        }

        private SqlProdureParameter() { }

        internal SqlProdureParameter(SqlCommand sqlcommand) {
            this.sqlcommand = sqlcommand;
        }
/*
        internal SqlProdureParameter(MySqlCommand mysqlcommand) {
            this.mysqlcommand = mysqlcommand;
        }
*/
        /// <summary>
        /// 释放
        /// </summary>
        internal void Dispose() {
            sqlcommand = null;
        }
    }

配置类

/*
 * 由SharpDevelop创建。
 * 用户: Administrator
 * 日期: 2019-03-21
 * 时间: 10:10
 * 
 * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
 */
using System;
using System.Net;

namespace MrWu.DB
{
	/// <summary>
	/// sql 配置
	/// </summary>
	public class SqlConfig
	{
		/// <summary>
		/// 
		/// </summary>
		public IPAddress host;
		
		/// <summary>
		/// 端口
		/// </summary>
		public int port;
		
		/// <summary>
		/// 用户名
		/// </summary>
		public string username;
		
		/// <summary>
		/// 密码
		/// </summary>
		public string pwd;

        /// <summary>
        /// 缓存链接数量
        /// </summary>
        public int cacheCount = 10;
	}
}

主要实现代码

/*
 * 由SharpDevelop创建。
 * 用户: Administrator
 * 日期: 2019-03-21
 * 时间: 10:07
 * 
 * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
 */
using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Text;

namespace MrWu.DB
{
	
	
	/// <summary>
	/// sqlServer 操作
	/// </summary>
	public class MMSQL
	{
		/// <summary>
		/// 链接池
		/// </summary>
		private class ConnectionPool{
			/// <summary>
			/// 链接字符串
			/// </summary>
			private readonly string connstr;

            /// <summary>
            /// 缓存链接数量
            /// </summary>
            private readonly int itemCount;
			
			/// <summary>
			/// 所有的链接
			/// </summary>
			private readonly ConcurrentQueue<SqlConnection> conns = new ConcurrentQueue<SqlConnection>();
			
			public ConnectionPool(SqlConfig config){
                this.itemCount = config.cacheCount;
				this.connstr = string.Format("Data Source={0},{1};user id={2};pwd={3};initial catalog=",
				                             config.host,config.port,config.username,config.pwd);
			}
			
            /// <summary>
            /// 获取一个连接
            /// </summary>
            /// <param name="dbbase"></param>
            /// <returns></returns>
			public SqlConnection GetConnection(string dbbase){
                SqlConnection conn;
                if (conns.TryDequeue(out conn)) {
                	if(conn.State == ConnectionState.Closed)
                		conn.Open();
                	try{
                    	conn.ChangeDatabase(dbbase); 
                    	//这里有可能被数据库主动关闭,客户端根本不知道,执行将会报错!不应该存储链接,用完即放入连接池,不然应用的时候也可能会报错,被服务器主动断开
                    }catch{
                    	conn = null;
					}
					if(conn != null)
                   		 return conn;
                }
                Console.WriteLine("创建一链接!" + conns.Count);
                return new SqlConnection(this.connstr + dbbase);
			}

            /// <summary>
            /// 放入一个连接
            /// </summary>
            /// <param name="conn"></param>
            public void Push(SqlConnection conn) {
                Task.Factory.StartNew(
                        () => {
                            if (conns.Count < itemCount) {
                                //Console.WriteLine("放入一个连接Begin");
                                conns.Enqueue(conn); //好像不会阻塞,Task可能是多余的,
                                //Console.WriteLine("放入一个链接!");
                            } else
                                conn.Dispose();
                        }
                    );
            }
		}
		
		/*
		 * 1.执行sql语句 获得执行sql语句后的结果
		 * 2.执行存储过程 获得存储过程的结果
		 * 
		 * 每个库都有一个链接
		 * 
		 * */
		
		private ConnectionPool ConnPool = null;
		
		/// <summary>
		/// 初始化
		/// </summary>
		/// <param name="config"></param>
		public MMSQL(SqlConfig config){
			this.ConnPool = new ConnectionPool(config);
		}
		
		/// <summary>
		/// 开始存储过程
		/// </summary>
		/// <param name="dbbase">存储过程所在的数据库</param>
		/// <param name="procedure">存储过程名</param>
		/// <returns></returns>
		public SqlProdureParameter BeginProcedure(string dbbase,string procedure){
			if(ConnPool == null)
				throw new Exception("you dont hava init");
			
			SqlCommand cmd = new SqlCommand(procedure, ConnPool.GetConnection(dbbase));
			SqlProdureParameter result = new SqlProdureParameter(cmd);
			//存储过程模式
			cmd.CommandType = System.Data.CommandType.StoredProcedure;
			return result;
		}
		
		/// <summary>
		/// 添加参数
		/// </summary>
		/// <param name="spp">存储过程查询键</param>
		/// <param name="paramName">参数名</param>
		/// <param name="value">参数值</param>
		/// <param name="type">参数类型</param>
		/// <param name="PD">参数种类</param>
		public void AddParam(SqlProdureParameter spp,string paramName,object value,SqlDbType type,ParameterDirection pd = ParameterDirection.Input){
			if(spp == null || spp.sqlcommand == null)
				throw new Exception("param spp null");
			SqlParameter param = spp.sqlcommand.Parameters.Add(paramName,type);
			param.Direction = pd;
            if (value == null)
                param.Value = DBNull.Value;
            else
			    param.Value = value;
		}
		
		/// <summary>
		/// 提交存储过程
		/// </summary>
		/// <param name="spp">存储过程查询键</param>
		/// <param name="ds">如果 存储过程中有查询表操作,则需要传递DataSet实例</param>
		/// <returns>参数值,输出参数从字典中获取</returns>
		public Dictionary<string,object> SubmitProcedure(SqlProdureParameter spp,DataSet ds = null){
			if(spp == null || spp.sqlcommand == null)
				throw new Exception("param spp null");

            Execute(spp.sqlcommand, ds);

            Dictionary<string,object> dic = new Dictionary<string,object>();
			var pms = spp.sqlcommand.Parameters;
			int len = pms.Count;
			for(int i=0;i<len;i++){
				dic.Add(pms[i].ParameterName,pms[i].Value);
			}
			spp.Dispose();
			return dic;
		}

        /// <summary>
        /// 执行sql语句
        /// </summary>
        /// <param name="dbbase">数据库</param>
        /// <param name="sql">sql语句</param>
        /// <param name="ds">如果是查询 使用传入ds实例</param>
        public void ExecuteSql(string dbbase,string sql,DataSet ds = null) {
            SqlCommand cmd = new SqlCommand(sql, ConnPool.GetConnection(dbbase));
            cmd.CommandType = CommandType.Text;
            Execute(cmd,ds);
        }

        /// <summary>
        /// 普通查询一个表
        /// </summary>
        /// <param name="dbbase">数据库名称</param>
        /// <param name="table">表名</param>
        /// <param name="column">列名</param>
        /// <param name="where">条件</param>
        /// <param name="count">查询的数量</param>
        /// <returns>查询到的表</returns>
        public DataTable Select(string dbbase,string table,string[] column = null,string where = null,int count = 0){
            StringBuilder sb = new StringBuilder();
            sb.Append("select ");
            if (count > 0) {
                sb.Append(string.Format("Top {0} ",count));
            }
            if (column != null && column.Length > 0) {
                int len = column.Length;
                for (int i=len-1;i>=0;i--) {
                    sb.Append(column[i]);
                    if (i > 0)
                        sb.Append(",");
                }
            } else {
                sb.Append("*");
            }

            sb.Append(" from ");
            sb.Append(table);
            if (!string.IsNullOrEmpty(where)) {
                sb.Append(" where ");
                sb.Append(where);
            }
            DataSet ds = new DataSet();

            Console.WriteLine("sql:" + sb.ToString());
            string ttt = sb.ToString();
            ExecuteSql(dbbase, sb.ToString(), ds);
            if (ds.Tables == null || ds.Tables.Count == 0)
                return null;
            return ds.Tables[0];
        }

        /// <summary>
        /// 执行sql命令
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="ds"></param>
        private void Execute(SqlCommand cmd,DataSet ds = null) {
            if (cmd.Connection.State == ConnectionState.Closed)
                cmd.Connection.Open();
            if (ds == null)
                cmd.ExecuteNonQuery();
            else {
                SqlDataAdapter sda = new SqlDataAdapter();
                sda.SelectCommand = cmd;
                sda.Fill(ds);   //调用会自动执行 ExecuteNonQuery();	不要重复调用,很容易出错!
                sda.Dispose();	//一定要关闭,不然下次执行报错;
            }
            ConnPool.Push(cmd.Connection);   //放入链接
        }

	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值