c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)

时间:2021-05-09 21:07:00

c#封装DBHelper类

 
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
    public enum EffentNextType
    {
        /// <summary>
        /// 对其他语句无任何影响
        /// </summary>
        None,
        /// <summary>
        /// 当前语句必须为"select count(1) from .."格式,如果存在则继续执行,不存在回滚事务
        /// </summary>
        WhenHaveContine,
        /// <summary>
        /// 当前语句必须为"select count(1) from .."格式,如果不存在则继续执行,存在回滚事务
        /// </summary>
        WhenNoHaveContine,
        /// <summary>
        /// 当前语句影响到的行数必须大于0,否则回滚事务
        /// </summary>
        ExcuteEffectRows,
        /// <summary>
        /// 引发事件-当前语句必须为"select count(1) from .."格式,如果不存在则继续执行,存在回滚事务
        /// </summary>
        SolicitationEvent
    }
    public class CommandInfo
    {
        public object ShareObject = null;
        public object OriginalData = null;
        event EventHandler _solicitationEvent;
        public event EventHandler SolicitationEvent
        {
            add
            {
                _solicitationEvent += value;
            }
            remove
            {
                _solicitationEvent -= value;
            }
        }
        public void OnSolicitationEvent()
        {
            if (_solicitationEvent != null)
            {
                _solicitationEvent(this, new EventArgs());
            }
        }
        public string CommandText;
        public System.Data.Common.DbParameter[] Parameters;
        public EffentNextType EffentNextType = EffentNextType.None;
        public CommandInfo()
        {

        }
        public CommandInfo(string sqlText, SqlParameter[] para)
        {
            this.CommandText = sqlText;
            this.Parameters = para;
        }
        public CommandInfo(string sqlText, SqlParameter[] para, EffentNextType type)
        {
            this.CommandText = sqlText;
            this.Parameters = para;
            this.EffentNextType = type;
        }
    }
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
  public abstract class DbHelper
    {
        public static string connectionString =ConfigurationManager.ConnectionStrings["Entity"].ConnectionString;

        #region 公用方法
        /// <summary>
        /// 判断是否存在某表的某个字段
        /// </summary>
        /// <param name="tableName">表名称</param>
        /// <param name="columnName">列名称</param>
        /// <returns>是否存在</returns>
        public static bool ColumnExists(string tableName, string columnName)
        {
            string sql = "select count(1) from syscolumns where [id]=object_id('" + tableName + "') and [name]='" + columnName + "'";
            object res = GetSingle(sql);
            if (res == null)
            {
                return false;
            }
            return Convert.ToInt32(res) > 0;
        }
        public static int GetMinID(string FieldName, string TableName)
        {
            string strsql = "select min(" + FieldName + ") from " + TableName;
            object obj = DbHelper.GetSingle(strsql);
            if (obj == null)
            {
                return 0;
            }
            else
            {
                return int.Parse(obj.ToString());
            }
        }
        public static int GetMaxID(string FieldName, string TableName)
        {
            string strsql = "select max(" + FieldName + ")+1 from " + TableName;
            object obj = DbHelper.GetSingle(strsql);
            if (obj == null)
            {
                return 1;
            }
            else
            {
                return int.Parse(obj.ToString());
            }
        }
        public static bool Exists(string strSql)
        {
            object obj = DbHelper.GetSingle(strSql);
            int cmdresult;
            if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
            {
                cmdresult = 0;
            }
            else
            {
                cmdresult = int.Parse(obj.ToString());
            }
            if (cmdresult == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        /// <summary>
        /// 表是否存在
        /// </summary>
        /// <param name="TableName"></param>
        /// <returns></returns>
        public static bool TabExists(string TableName)
        {
            string strsql = "select count(*) from sysobjects where id = object_id(N'[" + TableName + "]') and OBJECTPROPERTY(id, N'IsUserTable') = 1";
            //string strsql = "SELECT count(*) FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[" + TableName + "]') AND type in (N'U')";
            object obj = DbHelper.GetSingle(strsql);
            int cmdresult;
            if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
            {
                cmdresult = 0;
            }
            else
            {
                cmdresult = int.Parse(obj.ToString());
            }
            if (cmdresult == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        public static bool Exists(string strSql, params SqlParameter[] cmdParms)
        {
            object obj = DbHelper.GetSingle(strSql, cmdParms);
            int cmdresult;
            if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
            {
                cmdresult = 0;
            }
            else
            {
                cmdresult = int.Parse(obj.ToString());
            }
            if (cmdresult == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        #endregion

        #region  执行简单SQL语句

        /// <summary>
        /// 执行SQL语句,返回影响的记录数
        /// </summary>
        /// <param name="SQLString">SQL语句</param>
        /// <returns>影响的记录数</returns>
        public static int ExecuteSql(string SQLString)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                using (SqlCommand cmd = new SqlCommand(SQLString, connection))
                {
                    try
                    {
                        connection.Open();
                        int rows = cmd.ExecuteNonQuery();
                        return rows;
                    }
                    catch (System.Data.SqlClient.SqlException e)
                    {
                        connection.Close();
                        throw e;
                    }
                }
            }
        }

        /// <summary>
        /// 2012-2-21新增重载,执行SQL语句,返回影响的记录数
        /// </summary>
        /// <param name="connection">SqlConnection对象</param>
        /// <param name="trans">SqlTransaction事件</param>
        /// <param name="SQLString">SQL语句</param>
        /// <returns>影响的记录数</returns>
        public static int ExecuteSql(SqlConnection connection, SqlTransaction trans, string SQLString)
        {
            using (SqlCommand cmd = new SqlCommand(SQLString, connection))
            {
                try
                {
                    cmd.Connection = connection;
                    cmd.Transaction = trans;
                    int rows = cmd.ExecuteNonQuery();
                    return rows;
                }
                catch (System.Data.SqlClient.SqlException e)
                {
                    //trans.Rollback();
                    throw e;
                }
            }
        }

        public static int ExecuteSqlByTime(string SQLString, int Times)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                using (SqlCommand cmd = new SqlCommand(SQLString, connection))
                {
                    try
                    {
                        connection.Open();
                        cmd.CommandTimeout = Times;
                        int rows = cmd.ExecuteNonQuery();
                        return rows;
                    }
                    catch (System.Data.SqlClient.SqlException e)
                    {
                        connection.Close();
                        throw e;
                    }
                }
            }
        }

        /// <summary>
        /// 执行Sql和Oracle滴混合事务
        /// </summary>
        /// <param name="list">SQL命令行列表</param>
        /// <param name="oracleCmdSqlList">Oracle命令行列表</param>
        /// <returns>执行结果 0-由于SQL造成事务失败 -1 由于Oracle造成事务失败 1-整体事务执行成功</returns>
        public static int ExecuteSqlTran(List<CommandInfo> list, List<CommandInfo> oracleCmdSqlList)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
                SqlCommand cmd = new SqlCommand();
                cmd.Connection = conn;
                SqlTransaction tx = conn.BeginTransaction();
                cmd.Transaction = tx;
                try
                {
                    foreach (CommandInfo myDE in list)
                    {
                        string cmdText = myDE.CommandText;
                        SqlParameter[] cmdParms = (SqlParameter[])myDE.Parameters;
                        PrepareCommand(cmd, conn, tx, cmdText, cmdParms);
                        if (myDE.EffentNextType == EffentNextType.SolicitationEvent)
                        {
                            if (myDE.CommandText.ToLower().IndexOf("count(") == -1)
                            {
                                tx.Rollback();
                                throw new Exception("违背要求" + myDE.CommandText + "必须符合select count(..的格式");
                                //return 0;
                            }

                            object obj = cmd.ExecuteScalar();
                            bool isHave = false;
                            if (obj == null && obj == DBNull.Value)
                            {
                                isHave = false;
                            }
                            isHave = Convert.ToInt32(obj) > 0;
                            if (isHave)
                            {
                                //引发事件
                                myDE.OnSolicitationEvent();
                            }
                        }
                        if (myDE.EffentNextType == EffentNextType.WhenHaveContine || myDE.EffentNextType == EffentNextType.WhenNoHaveContine)
                        {
                            if (myDE.CommandText.ToLower().IndexOf("count(") == -1)
                            {
                                tx.Rollback();
                                throw new Exception("SQL:违背要求" + myDE.CommandText + "必须符合select count(..的格式");
                                //return 0;
                            }

                            object obj = cmd.ExecuteScalar();
                            bool isHave = false;
                            if (obj == null && obj == DBNull.Value)
                            {
                                isHave = false;
                            }
                            isHave = Convert.ToInt32(obj) > 0;

                            if (myDE.EffentNextType == EffentNextType.WhenHaveContine && !isHave)
                            {
                                tx.Rollback();
                                throw new Exception("SQL:违背要求" + myDE.CommandText + "返回值必须大于0");
                                //return 0;
                            }
                            if (myDE.EffentNextType == EffentNextType.WhenNoHaveContine && isHave)
                            {
                                tx.Rollback();
                                throw new Exception("SQL:违背要求" + myDE.CommandText + "返回值必须等于0");
                                //return 0;
                            }
                            continue;
                        }
                        int val = cmd.ExecuteNonQuery();
                        if (myDE.EffentNextType == EffentNextType.ExcuteEffectRows && val == 0)
                        {
                            tx.Rollback();
                            throw new Exception("SQL:违背要求" + myDE.CommandText + "必须有影响行");
                            //return 0;
                        }
                        cmd.Parameters.Clear();
                    }
                    tx.Commit();
                    return 1;
                }
                catch (SqlException e)
                {
                    tx.Rollback();
                    throw e;
                }
                catch (Exception e)
                {
                    tx.Rollback();
                    throw e;
                }
            }
        }
        /// <summary>
        /// 执行多条SQL语句,实现数据库事务。
        /// </summary>
        /// <param name="SQLStringList">多条SQL语句</param>
        public static int ExecuteSqlTran(List<String> SQLStringList)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
                SqlCommand cmd = new SqlCommand();
                cmd.Connection = conn;
                SqlTransaction tx = conn.BeginTransaction();
                cmd.Transaction = tx;
                try
                {
                    int count = 0;
                    for (int n = 0; n < SQLStringList.Count; n++)
                    {
                        string strsql = SQLStringList[n];
                        if (strsql.Trim().Length > 1)
                        {
                            cmd.CommandText = strsql;
                            count += cmd.ExecuteNonQuery();
                        }
                    }
                    tx.Commit();
                    return count;
                }
                catch
                {
                    tx.Rollback();
                    return 0;
                }
            }
        }
        /// <summary>
        /// 执行带一个存储过程参数的的SQL语句。
        /// </summary>
        /// <param name="SQLString">SQL语句</param>
        /// <param name="content">参数内容,比如一个字段是格式复杂的文章,有特殊符号,可以通过这个方式添加</param>
        /// <returns>影响的记录数</returns>
        public static int ExecuteSql(string SQLString, string content)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand cmd = new SqlCommand(SQLString, connection);
                System.Data.SqlClient.SqlParameter myParameter = new System.Data.SqlClient.SqlParameter("@content", SqlDbType.NText);
                myParameter.Value = content;
                cmd.Parameters.Add(myParameter);
                try
                {
                    connection.Open();
                    int rows = cmd.ExecuteNonQuery();
                    return rows;
                }
                catch (SqlException e)
                {
                    throw e;
                }
                finally
                {
                    cmd.Dispose();
                    connection.Close();
                }
            }
        }
        /// <summary>
        /// 执行带一个存储过程参数的的SQL语句。
        /// </summary>
        /// <param name="SQLString">SQL语句</param>
        /// <param name="content">参数内容,比如一个字段是格式复杂的文章,有特殊符号,可以通过这个方式添加</param>
        /// <returns>影响的记录数</returns>
        public static object ExecuteSqlGet(string SQLString, string content)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand cmd = new SqlCommand(SQLString, connection);
                System.Data.SqlClient.SqlParameter myParameter = new System.Data.SqlClient.SqlParameter("@content", SqlDbType.NText);
                myParameter.Value = content;
                cmd.Parameters.Add(myParameter);
                try
                {
                    connection.Open();
                    object obj = cmd.ExecuteScalar();
                    if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
                    {
                        return null;
                    }
                    else
                    {
                        return obj;
                    }
                }
                catch (System.Data.SqlClient.SqlException e)
                {
                    throw e;
                }
                finally
                {
                    cmd.Dispose();
                    connection.Close();
                }
            }
        }
        /// <summary>
        /// 向数据库里插入图像格式的字段(和上面情况类似的另一种实例)
        /// </summary>
        /// <param name="strSQL">SQL语句</param>
        /// <param name="fs">图像字节,数据库的字段类型为image的情况</param>
        /// <returns>影响的记录数</returns>
        public static int ExecuteSqlInsertImg(string strSQL, byte[] fs)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand cmd = new SqlCommand(strSQL, connection);
                System.Data.SqlClient.SqlParameter myParameter = new System.Data.SqlClient.SqlParameter("@fs", SqlDbType.Image);
                myParameter.Value = fs;
                cmd.Parameters.Add(myParameter);
                try
                {
                    connection.Open();
                    int rows = cmd.ExecuteNonQuery();
                    return rows;
                }
                catch (SqlException e)
                {
                    throw e;
                }
                finally
                {
                    cmd.Dispose();
                    connection.Close();
                }
            }
        }

        /// <summary>
        /// 执行一条计算查询结果语句,返回查询结果(object)。
        /// </summary>
        /// <param name="SQLString">计算查询结果语句</param>
        /// <returns>查询结果(object)</returns>
        public static object GetSingle(string SQLString)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                using (SqlCommand cmd = new SqlCommand(SQLString, connection))
                {
                    try
                    {
                        connection.Open();
                        object obj = cmd.ExecuteScalar();
                        if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
                        {
                            return null;
                        }
                        else
                        {
                            return obj;
                        }
                    }
                    catch (System.Data.SqlClient.SqlException e)
                    {
                        connection.Close();
                        throw e;
                    }
                }
            }
        }
        public static object GetSingle(string SQLString, int Times)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                using (SqlCommand cmd = new SqlCommand(SQLString, connection))
                {
                    try
                    {
                        connection.Open();
                        cmd.CommandTimeout = Times;
                        object obj = cmd.ExecuteScalar();
                        if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
                        {
                            return null;
                        }
                        else
                        {
                            return obj;
                        }
                    }
                    catch (System.Data.SqlClient.SqlException e)
                    {
                        connection.Close();
                        throw e;
                    }
                }
            }
        }
        /// <summary>
        /// 执行查询语句,返回SqlDataReader ( 注意:调用该方法后,一定要对SqlDataReader进行Close )
        /// </summary>
        /// <param name="strSQL">查询语句</param>
        /// <returns>SqlDataReader</returns>
        public static SqlDataReader ExecuteReader(string strSQL)
        {
            SqlConnection connection = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand(strSQL, connection);
            try
            {
                connection.Open();
                SqlDataReader myReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                return myReader;
            }
            catch (System.Data.SqlClient.SqlException e)
            {
                throw e;
            }

        }
        /// <summary>
        /// 执行查询语句,返回DataSet
        /// </summary>
        /// <param name="SQLString">查询语句</param>
        /// <returns>DataSet</returns>
        public static DataSet Query(string SQLString)
        {

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                DataSet ds = new DataSet();
                try
                {
                    connection.Open();
                    SqlDataAdapter command = new SqlDataAdapter(SQLString, connection);
                    command.Fill(ds, "ds");
                }
                catch (System.Data.SqlClient.SqlException ex)
                {
                    throw new Exception(ex.Message);
                }
                return ds;
            }

        }
        public static DataSet Query(string SQLString, int Times)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                DataSet ds = new DataSet();
                try
                {
                    connection.Open();
                    SqlDataAdapter command = new SqlDataAdapter(SQLString, connection);
                    command.SelectCommand.CommandTimeout = Times;
                    command.Fill(ds, "ds");
                }
                catch (System.Data.SqlClient.SqlException ex)
                {
                    throw new Exception(ex.Message);
                }
                return ds;
            }
        }

        /// <summary>
        ///新增重载,执行查询语句,返回DataSet
        /// </summary>
        /// <param name="connection">SqlConnection对象</param>
        /// <param name="trans">SqlTransaction事务</param>
        /// <param name="SQLString">SQL语句</param>
        /// <returns>DataSet</returns>
        public static DataSet Query(SqlConnection connection, SqlTransaction trans, string SQLString)
        {
            DataSet ds = new DataSet();
            try
            {
                SqlDataAdapter command = new SqlDataAdapter(SQLString, connection);
                command.SelectCommand.Transaction = trans;
                command.Fill(ds, "ds");
            }
            catch (SqlException ex)
            {
                throw new Exception(ex.Message);
            }
            return ds;

        }

        #endregion

        #region 执行带参数的SQL语句

        /// <summary>
        /// 执行SQL语句,返回影响的记录数
        /// </summary>
        /// <param name="SQLString">SQL语句</param>
        /// <returns>影响的记录数</returns>
        public static int ExecuteSql(string SQLString, params SqlParameter[] cmdParms)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    try
                    {
                        PrepareCommand(cmd, connection, null, SQLString, cmdParms);
                        int rows = cmd.ExecuteNonQuery();
                        cmd.Parameters.Clear();
                        return rows;
                    }
                    catch (SqlException e)
                    {
                        throw e;
                    }
                }
            }
        }

        /// <summary>
        /// 新增重载,执行SQL语句,返回影响的记录数
        /// </summary>
        /// <param name="connection">SqlConnection对象</param>
        /// <param name="trans">SqlTransaction对象</param>
        /// <param name="SQLString">SQL语句</param>
        /// <returns>影响的记录数</returns>
        public static int ExecuteSql(SqlConnection connection, SqlTransaction trans, string SQLString, params SqlParameter[] cmdParms)
        {
            using (SqlCommand cmd = new SqlCommand())
            {
                try
                {
                    PrepareCommand(cmd, connection, trans, SQLString, cmdParms);
                    int rows = cmd.ExecuteNonQuery();
                    cmd.Parameters.Clear();
                    return rows;
                }
                catch (System.Data.SqlClient.SqlException e)
                {
                    //trans.Rollback();
                    throw e;
                }
            }
        }

        /// <summary>
        /// 执行多条SQL语句,实现数据库事务。
        /// </summary>
        /// <param name="SQLStringList">SQL语句的哈希表(key为sql语句,value是该语句的SqlParameter[])</param>
        public static void ExecuteSqlTran(Hashtable SQLStringList)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
                using (SqlTransaction trans = conn.BeginTransaction())
                {
                    SqlCommand cmd = new SqlCommand();
                    try
                    {
                        //循环
                        foreach (DictionaryEntry myDE in SQLStringList)
                        {
                            string cmdText = myDE.Key.ToString();
                            SqlParameter[] cmdParms = (SqlParameter[])myDE.Value;
                            PrepareCommand(cmd, conn, trans, cmdText, cmdParms);
                            int val = cmd.ExecuteNonQuery();
                            cmd.Parameters.Clear();
                        }
                        trans.Commit();
                    }
                    catch
                    {
                        trans.Rollback();
                        throw;
                    }
                }
            }
        }
        /// <summary>
        /// 执行多条SQL语句,实现数据库事务。
        /// </summary>
        /// <param name="SQLStringList">SQL语句的哈希表(key为sql语句,value是该语句的SqlParameter[])</param>
        public static int ExecuteSqlTran(System.Collections.Generic.List<CommandInfo> cmdList)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
                using (SqlTransaction trans = conn.BeginTransaction())
                {
                    SqlCommand cmd = new SqlCommand();
                    try
                    {
                        int count = 0;
                        //循环
                        foreach (CommandInfo myDE in cmdList)
                        {
                            string cmdText = myDE.CommandText;
                            SqlParameter[] cmdParms = (SqlParameter[])myDE.Parameters;
                            PrepareCommand(cmd, conn, trans, cmdText, cmdParms);

                            if (myDE.EffentNextType == EffentNextType.WhenHaveContine || myDE.EffentNextType == EffentNextType.WhenNoHaveContine)
                            {
                                if (myDE.CommandText.ToLower().IndexOf("count(") == -1)
                                {
                                    trans.Rollback();
                                    return 0;
                                }

                                object obj = cmd.ExecuteScalar();
                                bool isHave = false;
                                if (obj == null && obj == DBNull.Value)
                                {
                                    isHave = false;
                                }
                                isHave = Convert.ToInt32(obj) > 0;

                                if (myDE.EffentNextType == EffentNextType.WhenHaveContine && !isHave)
                                {
                                    trans.Rollback();
                                    return 0;
                                }
                                if (myDE.EffentNextType == EffentNextType.WhenNoHaveContine && isHave)
                                {
                                    trans.Rollback();
                                    return 0;
                                }
                                continue;
                            }
                            int val = cmd.ExecuteNonQuery();
                            count += val;
                            if (myDE.EffentNextType == EffentNextType.ExcuteEffectRows && val == 0)
                            {
                                trans.Rollback();
                                return 0;
                            }
                            cmd.Parameters.Clear();
                        }
                        trans.Commit();
                        return count;
                    }
                    catch
                    {
                        trans.Rollback();
                        throw;
                    }
                }
            }
        }
        /// <summary>
        /// 执行多条SQL语句,实现数据库事务。
        /// </summary>
        /// <param name="SQLStringList">SQL语句的哈希表(key为sql语句,value是该语句的SqlParameter[])</param>
        public static void ExecuteSqlTranWithIndentity(System.Collections.Generic.List<CommandInfo> SQLStringList)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
                using (SqlTransaction trans = conn.BeginTransaction())
                {
                    SqlCommand cmd = new SqlCommand();
                    try
                    {
                        int indentity = 0;
                        //循环
                        foreach (CommandInfo myDE in SQLStringList)
                        {
                            string cmdText = myDE.CommandText;
                            SqlParameter[] cmdParms = (SqlParameter[])myDE.Parameters;
                            foreach (SqlParameter q in cmdParms)
                            {
                                if (q.Direction == ParameterDirection.InputOutput)
                                {
                                    q.Value = indentity;
                                }
                            }
                            PrepareCommand(cmd, conn, trans, cmdText, cmdParms);
                            int val = cmd.ExecuteNonQuery();
                            foreach (SqlParameter q in cmdParms)
                            {
                                if (q.Direction == ParameterDirection.Output)
                                {
                                    indentity = Convert.ToInt32(q.Value);
                                }
                            }
                            cmd.Parameters.Clear();
                        }
                        trans.Commit();
                    }
                    catch
                    {
                        trans.Rollback();
                        throw;
                    }
                }
            }
        }
        /// <summary>
        /// 执行多条SQL语句,实现数据库事务。
        /// </summary>
        /// <param name="SQLStringList">SQL语句的哈希表(key为sql语句,value是该语句的SqlParameter[])</param>
        public static void ExecuteSqlTranWithIndentity(Hashtable SQLStringList)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
                using (SqlTransaction trans = conn.BeginTransaction())
                {
                    SqlCommand cmd = new SqlCommand();
                    try
                    {
                        int indentity = 0;
                        //循环
                        foreach (DictionaryEntry myDE in SQLStringList)
                        {
                            string cmdText = myDE.Key.ToString();
                            SqlParameter[] cmdParms = (SqlParameter[])myDE.Value;
                            foreach (SqlParameter q in cmdParms)
                            {
                                if (q.Direction == ParameterDirection.InputOutput)
                                {
                                    q.Value = indentity;
                                }
                            }
                            PrepareCommand(cmd, conn, trans, cmdText, cmdParms);
                            int val = cmd.ExecuteNonQuery();
                            foreach (SqlParameter q in cmdParms)
                            {
                                if (q.Direction == ParameterDirection.Output)
                                {
                                    indentity = Convert.ToInt32(q.Value);
                                }
                            }
                            cmd.Parameters.Clear();
                        }
                        trans.Commit();
                    }
                    catch
                    {
                        trans.Rollback();
                        throw;
                    }
                }
            }
        }
        /// <summary>
        /// 执行一条计算查询结果语句,返回查询结果(object)。
        /// </summary>
        /// <param name="SQLString">计算查询结果语句</param>
        /// <returns>查询结果(object)</returns>
        public static object GetSingle(string SQLString, params SqlParameter[] cmdParms)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    try
                    {
                        PrepareCommand(cmd, connection, null, SQLString, cmdParms);
                        object obj = cmd.ExecuteScalar();
                        cmd.Parameters.Clear();
                        if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
                        {
                            return null;
                        }
                        else
                        {
                            return obj;
                        }
                    }
                    catch (System.Data.SqlClient.SqlException e)
                    {
                        throw e;
                    }
                }
            }
        }

        /// <summary>
        /// 新增重载,执行一条计算查询结果语句,返回查询结果(object)。
        /// </summary>
        /// <param name="connection">SqlConnection对象</param>
        /// <param name="trans">SqlTransaction事务</param>
        /// <param name="SQLString">计算查询结果语句</param>
        /// <returns>查询结果(object)</returns>
        public static object GetSingle(SqlConnection connection, SqlTransaction trans, string SQLString, params SqlParameter[] cmdParms)
        {
            using (SqlCommand cmd = new SqlCommand())
            {
                try
                {
                    PrepareCommand(cmd, connection, trans, SQLString, cmdParms);
                    object obj = cmd.ExecuteScalar();
                    cmd.Parameters.Clear();
                    if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
                    {
                        return null;
                    }
                    else
                    {
                        return obj;
                    }
                }
                catch (System.Data.SqlClient.SqlException e)
                {
                    trans.Rollback();
                    throw e;
                }
            }
        }

        /// <summary>
        /// 执行查询语句,返回SqlDataReader ( 注意:调用该方法后,一定要对SqlDataReader进行Close )
        /// </summary>
        /// <param name="strSQL">查询语句</param>
        /// <returns>SqlDataReader</returns>
        public static SqlDataReader ExecuteReader(string SQLString, params SqlParameter[] cmdParms)
        {
            SqlConnection connection = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand();
            try
            {
                PrepareCommand(cmd, connection, null, SQLString, cmdParms);
                SqlDataReader myReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                cmd.Parameters.Clear();
                return myReader;
            }
            catch (System.Data.SqlClient.SqlException e)
            {
                throw e;
            }
            //            finally
            //            {
            //                cmd.Dispose();
            //                connection.Close();
            //            }    

        }

        /// <summary>
        /// 执行查询语句,返回DataSet
        /// </summary>
        /// <param name="SQLString">查询语句</param>
        /// <returns>DataSet</returns>
        public static DataSet Query(string SQLString, params SqlParameter[] cmdParms)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand cmd = new SqlCommand();
                PrepareCommand(cmd, connection, null, SQLString, cmdParms);
                using (SqlDataAdapter da = new SqlDataAdapter(cmd))
                {
                    DataSet ds = new DataSet();
                    try
                    {
                        da.Fill(ds, "ds");
                        cmd.Parameters.Clear();
                    }
                    catch (System.Data.SqlClient.SqlException ex)
                    {
                        throw new Exception(ex.Message);
                    }
                    return ds;
                }
            }
        }

        /// <summary>
        /// 新增重载,执行查询语句,返回DataSet
        /// </summary>
        /// <param name="connection">SqlConnection对象</param>
        /// <param name="trans">SqlTransaction事务</param>
        /// <param name="SQLString">查询语句</param>
        /// <returns>DataSet</returns>
        public static DataSet Query(SqlConnection connection, SqlTransaction trans, string SQLString, params SqlParameter[] cmdParms)
        {
            SqlCommand cmd = new SqlCommand();
            PrepareCommand(cmd, connection, trans, SQLString, cmdParms);
            using (SqlDataAdapter da = new SqlDataAdapter(cmd))
            {
                DataSet ds = new DataSet();
                try
                {
                    da.Fill(ds, "ds");
                    cmd.Parameters.Clear();
                }
                catch (System.Data.SqlClient.SqlException ex)
                {
                    trans.Rollback();
                    throw new Exception(ex.Message);
                }
                return ds;
            }
        }

        private static void PrepareCommand(SqlCommand cmd, SqlConnection conn, SqlTransaction trans, string cmdText, SqlParameter[] cmdParms)
        {
            if (conn.State != ConnectionState.Open)
                conn.Open();
            cmd.Connection = conn;
            cmd.CommandText = cmdText;
            if (trans != null)
                cmd.Transaction = trans;
            cmd.CommandType = CommandType.Text;//cmdType;
            if (cmdParms != null)
            {

                foreach (SqlParameter parameter in cmdParms)
                {
                    if ((parameter.Direction == ParameterDirection.InputOutput || parameter.Direction == ParameterDirection.Input) &&
                        (parameter.Value == null))
                    {
                        parameter.Value = DBNull.Value;
                    }
                    cmd.Parameters.Add(parameter);
                }
            }
        }

        #endregion

        #region 存储过程操作

        /// <summary>
        /// 执行存储过程,返回SqlDataReader ( 注意:调用该方法后,一定要对SqlDataReader进行Close )
        /// </summary>
        /// <param name="storedProcName">存储过程名</param>
        /// <param name="parameters">存储过程参数</param>
        /// <returns>SqlDataReader</returns>
        public static SqlDataReader RunProcedure(string storedProcName, IDataParameter[] parameters)
        {
            SqlConnection connection = new SqlConnection(connectionString);
            SqlDataReader returnReader;
            connection.Open();
            SqlCommand command = BuildQueryCommand(connection, storedProcName, parameters);
            command.CommandType = CommandType.StoredProcedure;
            returnReader = command.ExecuteReader(CommandBehavior.CloseConnection);
            return returnReader;

        }

        /// <summary>
        /// 执行存储过程
        /// </summary>
        /// <param name="storedProcName">存储过程名</param>
        /// <param name="parameters">存储过程参数</param>
        /// <param name="tableName">DataSet结果中的表名</param>
        /// <returns>DataSet</returns>
        public static DataSet RunProcedure(string storedProcName, IDataParameter[] parameters, string tableName)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                DataSet dataSet = new DataSet();
                connection.Open();
                SqlDataAdapter sqlDA = new SqlDataAdapter();
                sqlDA.SelectCommand = BuildQueryCommand(connection, storedProcName, parameters);
                sqlDA.Fill(dataSet, tableName);
                connection.Close();
                return dataSet;
            }
        }
        public static DataSet RunProcedure(string storedProcName, IDataParameter[] parameters, string tableName, int Times)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                DataSet dataSet = new DataSet();
                connection.Open();
                SqlDataAdapter sqlDA = new SqlDataAdapter();
                sqlDA.SelectCommand = BuildQueryCommand(connection, storedProcName, parameters);
                sqlDA.SelectCommand.CommandTimeout = Times;
                sqlDA.Fill(dataSet, tableName);
                connection.Close();
                return dataSet;
            }
        }

        /// <summary>
        /// 构建 SqlCommand 对象(用来返回一个结果集,而不是一个整数值)
        /// </summary>
        /// <param name="connection">数据库连接</param>
        /// <param name="storedProcName">存储过程名</param>
        /// <param name="parameters">存储过程参数</param>
        /// <returns>SqlCommand</returns>
        private static SqlCommand BuildQueryCommand(SqlConnection connection, string storedProcName, IDataParameter[] parameters)
        {
            SqlCommand command = new SqlCommand(storedProcName, connection);
            command.CommandType = CommandType.StoredProcedure;
            foreach (SqlParameter parameter in parameters)
            {
                if (parameter != null)
                {
                    // 检查未分配值的输出参数,将其分配以DBNull.Value.
                    if ((parameter.Direction == ParameterDirection.InputOutput || parameter.Direction == ParameterDirection.Input) &&
                        (parameter.Value == null))
                    {
                        parameter.Value = DBNull.Value;
                    }
                    command.Parameters.Add(parameter);
                }
            }

            return command;
        }

        /// <summary>
        /// 执行存储过程,返回影响的行数
        /// </summary>
        /// <param name="storedProcName">存储过程名</param>
        /// <param name="parameters">存储过程参数</param>
        /// <param name="rowsAffected">影响的行数</param>
        /// <returns></returns>
        public static int RunProcedure(string storedProcName, IDataParameter[] parameters, out int rowsAffected)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                int result;
                connection.Open();
                SqlCommand command = BuildIntCommand(connection, storedProcName, parameters);
                rowsAffected = command.ExecuteNonQuery();
                result = (int)command.Parameters["ReturnValue"].Value;
                //Connection.Close();
                return result;
            }
        }

        /// <summary>
        /// 创建 SqlCommand 对象实例(用来返回一个整数值)
        /// </summary>
        /// <param name="storedProcName">存储过程名</param>
        /// <param name="parameters">存储过程参数</param>
        /// <returns>SqlCommand 对象实例</returns>
        private static SqlCommand BuildIntCommand(SqlConnection connection, string storedProcName, IDataParameter[] parameters)
        {
            SqlCommand command = BuildQueryCommand(connection, storedProcName, parameters);
            command.Parameters.Add(new SqlParameter("ReturnValue",
                SqlDbType.Int, 4, ParameterDirection.ReturnValue,
                false, 0, 0, string.Empty, DataRowVersion.Default, null));
            return command;
        }
        #endregion

    }
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

c# 图片加水印

 
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
        /// <summary>
        /// 图片水印
        /// </summary>
        /// <param name="imgPath">服务器图片相对路径</param>
        /// <param name="filename">保存文件名</param>
        /// <param name="watermarkFilename">水印文件相对路径</param>
        /// <param name="watermarkStatus">图片水印位置 0=不使用 1=左上 2=中上 3=右上 4=左中  9=右下</param>
        /// <param name="quality">附加水印图片质量,0-100</param>
        /// <param name="watermarkTransparency">水印的透明度 1--10 10为不透明</param>
        public static void AddImageSignPic(string imgPath, string filename, string watermarkFilename, int watermarkStatus, int quality, int watermarkTransparency)
        {
            if (!File.Exists(Utils.GetMapPath(imgPath)))
                return;
            byte[] _ImageBytes = File.ReadAllBytes(Utils.GetMapPath(imgPath));
            Image img = Image.FromStream(new System.IO.MemoryStream(_ImageBytes));
            filename = Utils.GetMapPath(filename);

            if (watermarkFilename.StartsWith("/") == false)
                watermarkFilename = "/" + watermarkFilename;
            watermarkFilename = Utils.GetMapPath(watermarkFilename);
            if (!File.Exists(watermarkFilename))
                return;
            Graphics g = Graphics.FromImage(img);
            //设置高质量插值法
            //g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
            //设置高质量,低速度呈现平滑程度
            //g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            Image watermark = new Bitmap(watermarkFilename);

            if (watermark.Height >= img.Height || watermark.Width >= img.Width)
                return;

            ImageAttributes imageAttributes = new ImageAttributes();
            ColorMap colorMap = new ColorMap();

            colorMap.OldColor = Color.FromArgb(255, 0, 255, 0);
            colorMap.NewColor = Color.FromArgb(0, 0, 0, 0);
            ColorMap[] remapTable = { colorMap };

            imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap);

            float transparency = 0.5F;
            if (watermarkTransparency >= 1 && watermarkTransparency <= 10)
                transparency = (watermarkTransparency / 10.0F);

            float[][] colorMatrixElements = {
                                                new float[] {1.0f,  0.0f,  0.0f,  0.0f, 0.0f},
                                                new float[] {0.0f,  1.0f,  0.0f,  0.0f, 0.0f},
                                                new float[] {0.0f,  0.0f,  1.0f,  0.0f, 0.0f},
                                                new float[] {0.0f,  0.0f,  0.0f,  transparency, 0.0f},
                                                new float[] {0.0f,  0.0f,  0.0f,  0.0f, 1.0f}
                                            };

            ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);

            imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

            int xpos = 0;
            int ypos = 0;

            switch (watermarkStatus)
            {
                case 1:
                    xpos = (int)(img.Width * (float).01);
                    ypos = (int)(img.Height * (float).01);
                    break;
                case 2:
                    xpos = (int)((img.Width * (float).50) - (watermark.Width / 2));
                    ypos = (int)(img.Height * (float).01);
                    break;
                case 3:
                    xpos = (int)((img.Width * (float).99) - (watermark.Width));
                    ypos = (int)(img.Height * (float).01);
                    break;
                case 4:
                    xpos = (int)(img.Width * (float).01);
                    ypos = (int)((img.Height * (float).50) - (watermark.Height / 2));
                    break;
                case 5:
                    xpos = (int)((img.Width * (float).50) - (watermark.Width / 2));
                    ypos = (int)((img.Height * (float).50) - (watermark.Height / 2));
                    break;
                case 6:
                    xpos = (int)((img.Width * (float).99) - (watermark.Width));
                    ypos = (int)((img.Height * (float).50) - (watermark.Height / 2));
                    break;
                case 7:
                    xpos = (int)(img.Width * (float).01);
                    ypos = (int)((img.Height * (float).99) - watermark.Height);
                    break;
                case 8:
                    xpos = (int)((img.Width * (float).50) - (watermark.Width / 2));
                    ypos = (int)((img.Height * (float).99) - watermark.Height);
                    break;
                case 9:
                    xpos = (int)((img.Width * (float).99) - (watermark.Width));
                    ypos = (int)((img.Height * (float).99) - watermark.Height);
                    break;
            }

            g.DrawImage(watermark, new Rectangle(xpos, ypos, watermark.Width, watermark.Height), 0, 0, watermark.Width, watermark.Height, GraphicsUnit.Pixel, imageAttributes);

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo ici = null;
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.MimeType.IndexOf("jpeg") > -1)
                    ici = codec;
            }
            EncoderParameters encoderParams = new EncoderParameters();
            long[] qualityParam = new long[1];
            if (quality < 0 || quality > 100)
                quality = 80;

            qualityParam[0] = quality;

            EncoderParameter encoderParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qualityParam);
            encoderParams.Param[0] = encoderParam;

            if (ici != null)
                img.Save(filename, ici, encoderParams);
            else
                img.Save(filename);

            g.Dispose();
            img.Dispose();
            watermark.Dispose();
            imageAttributes.Dispose();
        }
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
        /// <summary>
        /// 文字水印
        /// </summary>
        /// <param name="imgPath">服务器图片相对路径</param>
        /// <param name="filename">保存文件名</param>
        /// <param name="watermarkText">水印文字</param>
        /// <param name="watermarkStatus">图片水印位置 0=不使用 1=左上 2=中上 3=右上 4=左中  9=右下</param>
        /// <param name="quality">附加水印图片质量,0-100</param>
        /// <param name="fontname">字体</param>
        /// <param name="fontsize">字体大小</param>
        public static void AddImageSignText(string imgPath, string filename, string watermarkText, int watermarkStatus, int quality, string fontname, int fontsize)
        {
            byte[] _ImageBytes = File.ReadAllBytes(Utils.GetMapPath(imgPath));
            Image img = Image.FromStream(new System.IO.MemoryStream(_ImageBytes));
            filename = Utils.GetMapPath(filename);

            Graphics g = Graphics.FromImage(img);
            Font drawFont = new Font(fontname, fontsize, FontStyle.Regular, GraphicsUnit.Pixel);
            SizeF crSize;
            crSize = g.MeasureString(watermarkText, drawFont);

            float xpos = 0;
            float ypos = 0;

            switch (watermarkStatus)
            {
                case 1:
                    xpos = (float)img.Width * (float).01;
                    ypos = (float)img.Height * (float).01;
                    break;
                case 2:
                    xpos = ((float)img.Width * (float).50) - (crSize.Width / 2);
                    ypos = (float)img.Height * (float).01;
                    break;
                case 3:
                    xpos = ((float)img.Width * (float).99) - crSize.Width;
                    ypos = (float)img.Height * (float).01;
                    break;
                case 4:
                    xpos = (float)img.Width * (float).01;
                    ypos = ((float)img.Height * (float).50) - (crSize.Height / 2);
                    break;
                case 5:
                    xpos = ((float)img.Width * (float).50) - (crSize.Width / 2);
                    ypos = ((float)img.Height * (float).50) - (crSize.Height / 2);
                    break;
                case 6:
                    xpos = ((float)img.Width * (float).99) - crSize.Width;
                    ypos = ((float)img.Height * (float).50) - (crSize.Height / 2);
                    break;
                case 7:
                    xpos = (float)img.Width * (float).01;
                    ypos = ((float)img.Height * (float).99) - crSize.Height;
                    break;
                case 8:
                    xpos = ((float)img.Width * (float).50) - (crSize.Width / 2);
                    ypos = ((float)img.Height * (float).99) - crSize.Height;
                    break;
                case 9:
                    xpos = ((float)img.Width * (float).99) - crSize.Width;
                    ypos = ((float)img.Height * (float).99) - crSize.Height;
                    break;
            }

            g.DrawString(watermarkText, drawFont, new SolidBrush(Color.White), xpos + 1, ypos + 1);
            g.DrawString(watermarkText, drawFont, new SolidBrush(Color.Black), xpos, ypos);

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo ici = null;
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.MimeType.IndexOf("jpeg") > -1)
                    ici = codec;
            }
            EncoderParameters encoderParams = new EncoderParameters();
            long[] qualityParam = new long[1];
            if (quality < 0 || quality > 100)
                quality = 80;

            qualityParam[0] = quality;

            EncoderParameter encoderParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qualityParam);
            encoderParams.Param[0] = encoderParam;

            if (ici != null)
                img.Save(filename, ici, encoderParams);
            else
                img.Save(filename);

            g.Dispose();
            img.Dispose();
        }
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

(摘)C#生成随机数的三种方法

 

随机数的定义为:产生的所有数字毫无关系.

在实际应用中很多地方会用到随机数,比如需要生成唯一的订单号.

在C#中获取随机数有三种方法:

一.Random 类

Random类默认的无参构造函数可以根据当前系统时钟为种子,进行一系列算法得出要求范围内的伪随机数.

Random rd = new Random();
int i = rd.Next();

这种随机数可以达到一些要求较低的目标,但是如果在高并发的情况下,Random类所取到的系统时钟种子接近甚至完全一样,就很有可能出现重复,这里用循环来举例

for (int i = 0; i < 10; i++)
{
    Random rd = new Random();  //无参即为使用系统时钟为种子
    Console.WriteLine(rd.Next().ToString());
}

这个例子会输出10个相同的"随机数".

突显出的问题:因为Random进行伪随机数的算法是固定的,所以根据同一个种子计算出的数字必然是一样的.而以当代计算机的运行速度,该循环几乎是在瞬间完成的,种子一致,所以会出现10次循环输出同一随机数的情况.

有的时候使用random生成随机数的时候往往不是随机的 这是为什么呢?

随机数生成方法可以说是任何编程语言必备的功能,它的重要性不言而言,在C#中我们通常使用Random类生成随机数,在一些场景下,我却发现Random生成的随机数并不可靠,在下面的例子中我们通过循环随机生成5个随机数:

for (int i = 0; i < 5; i++) {     Random random = new Random();     Console.WriteLine(random.Next()); }

这段代码执行后的结果如下所示:

2140400647 2140400647 2140400647 2140400647 2140400647

通过以上结果可知,随机数类生成了5个相同的数,这并非我们的预期,为什么呢?为了弄清楚这个问题,零度剖析了微软官方的开源Random类,发现在C#中生成随机数使用的算法是线性同余法,经百科而知,这种算法生成的不是绝对随机,而是一种伪随机数,线性同余法算法的的公式是:

第N+1个数 = ( 第N个数 * A + B) % M

上面的公式中A、B和M分别为常数,是生成随机数的因子,如果之前从未通过同一个Random对象生成过随机数(也就是调用过Next方法),那么第N个随机数为将被指定为一个默认的常数,这个常数在创建一个Random类时被默认值指定,Random也提供一个构造函数允许开发者使用自己的随机数因子,这一切可通过微软官方开源代码看到:

public Random() : this(Environment.TickCount) { }  public Random(int Seed) { }

通过默认构造函数创建Random类时,一个Environment.TickCount对象作为因子被默认传递给第二个构造函数,Environment.TickCount表示操作系统启动后经过的毫秒数,计算机的运算运算速度远比毫秒要快得多,这导致一个的具有毫秒精度的因子参与随机数的生成过程,但在5次循环中,我们使用了同一个毫秒级的因子,从而生成相同的随机数,另外,第N+1个数的生成与第N个数有着直接的关系。

在上面的例子中,假设系统启动以来的毫秒数为888毫秒,执行5次循环用时只有0.1毫秒,这导致在循环中创建的5个Random对象都使用了相同的888因子,每次被创建的随机对象又使用了相同的第N个数(默认为常数),通过这样的假设我们不难看出,上面的结果是必然的。

现在我们改变这个格局,在循环之外创建一个Random对象,在每次循环中引用它,并通过它生成随机数,并在同一个对象上多次调用Next方法,从而不断变化第N个数,代码如下所示:

Random random = new Random();  for (int i = 0; i < 5; i++) {     Console.WriteLine(random.Next()); }

执行后的结果如下所示:

391098894 1791722821 1488616582 1970032058 201874423

我们看到这个结果确实证实了我们上面的推断,第1次循环时公式中的第N个数为默认常数;当第二次循环时,第N个数为391098894,随后不断变化的第N个数作为因子参与计算,这保证了结果的随机性。

虽然通过我们的随机数看起来也很随机了,但必定这个算法是伪随机数,当第N个数和因子都相同时,生成的随机数仍然是重复的随机数,由于Random提供一个带参的构造函数允许我们传入一个因子,如果传入的因子随机性强的话,那么生成的随机数也会比较可靠,为了提供一个可靠点的因子,我们通常使用GUID产生填充因子,同样放在循环中测试:

for (int i = 0; i < 5; i++) {     byte[] buffer = Guid.NewGuid().ToByteArray();     int iSeed = BitConverter.ToInt32(buffer, 0);     Random random = new Random(iSeed);     Console.WriteLine(random.Next()); }

这样的方式保证了填充因子的随机性,所以生成的随机数也比较可靠,运行结果如下所示:

734397360 1712793171 1984332878 819811856 1015979983

在一些场景下这样的随机数并不可靠,为了生成更加可靠的随机数,微软在System.Security.Cryptography命名空间下提供一个名为RNGCryptoServiceProvider的类,它采用系统当前的硬件信息、进程信息、线程信息、系统启动时间和当前精确时间作为填充因子,通过更好的算法生成高质量的随机数,它的使用方法如下所示:

byte[] randomBytes = new byte[4]; RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider(); rngServiceProvider.GetBytes(randomBytes); Int32 result = BitConverter.ToInt32(randomBytes, 0);

通过这种算法生成的随机数,经过成千上万次的测试,并未发现重复,质量的确比Random高了很多。另外windows api也提供了一个非托管的随机数生成函数CryptGenRandom,CryptGenRandom与RNGCryptoServiceProvider的原理类似,采用C++编写,如果要在.NET中使用,需要进行简单的封装。它的原型如下所示:

BOOL WINAPI CryptGenRandom(   _In_     HCRYPTPROV hProv,   _In_     DWORD dwLen,   _Inout_  BYTE *pbBuffer );

以上就是零度为您带来的随机数生成方法和基本原理,您可以通过需求和场景选择最佳的方式,Random算法简单,性能较高,适用于随机性要求不高的情况,由于RNGCryptoServiceProvider在生成期间需要查询上面提到的几种系统因子,所以性能稍弱于Random类,但随机数质量高,可靠性更好。

 二.Guid 类

System.Guid

GUID (Globally Unique Identifier) 全球唯一标识符

GUID的计算使用到了很多在本机可取到的数字,如硬件的ID码,当前时间等.所计算出的128位整数(16字节)可以接近唯一的输出.

Console.WriteLine(Guid.NewGuid().ToString());

计算结果是xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx结构的16进制数字.当然这个格式也是可以更改的.

三.RNGCryptoServiceProvider 类

System.Security.Cryptography.RNGCryptoServiceProvider

RNGCryptoServiceProvider 使用加密服务提供程序 (CSP) 提供的实现来实现加密随机数生成器 (RNG)

RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();
byte[] byteCsp = new byte[10];
csp.GetBytes(byteCsp);
Console.WriteLine(BitConverter.ToString(byteCsp));

因该类使用更严密的算法.所以即使如下放在循环中,所计算出的随机数也是不同的.

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
for (int i = 0; i < 10; i++)
{
    RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();
    byte[] byteCsp = new byte[10];
    csp.GetBytes(byteCsp);
    Console.WriteLine(BitConverter.ToString(byteCsp));
}//但是RNGCryptoServiceProvider的计算较为繁琐,在循环中使用会消耗造成大量的系统资源开销,使用时需注意.
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
Membership.GeneratePassword()

Membership是一个方便快捷的进行角色权限管理的类,偶然发现一个很有意思的方法,没研究过是如何实现的

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
public static string GeneratePassword(int length, int numberOfNonAlphanumericCharacters);
//
// 摘要:
//     生成指定长度的随机密码。
//
// 参数:
//   numberOfNonAlphanumericCharacters:
//     生成的密码中的标点字符数。
//
//   length:
//     生成的密码的字符数。长度必须介于 1 和 128 个字符之间。
//
// 返回结果:
//     指定长度的随机密码。
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

例:

for (int i = 0; i < 10; i++)
{
    Response.Write(Membership.GeneratePassword(20, 1) + "<br>");
}

结果为

C!&^HoTNv3!ZHkK9BAbu

azLgER)JJ-UW8q*14yz*

I3qnb]Zxu16ht!kKZ!Q*

9U:MAQ&c1x)^aed@xe**

oL(%4JvfbP&t5*Hpl4l-

6@zj$CnhW&D+|xOf:qIk

A/!Di&l*tY$QaMH0gyzY

z^wu6{1BMq7D^+WU]>f$

1OgIJS3&09fw0F9.|aXA

8F+Gy+L{O6x{SfugME*%

原文:https://www.cnblogs.com/xiaowie/p/8759837.html

(摘)timeout Timeout时间已到.在操作完成之前超时时间已过或服务器未响应的几种情况

 

Timeout时间已到.在操作完成之前超时时间已过或服务器未响应

问题

在使用asp.net开发的应用程序查询数据的时候,遇到页面请求时间过长且返回"Timeout时间已到。在操作完成之间超时时间已过或服务器未响应"的情况

分析

造成这一问题的原因大概有以下几点:  
   1.Asp.net请求超时      
   2.Webservice请求超时       
   3.IIS请求超时       
   4.数据库连接超时

数据库连接超时

连接字符串中添加Connect Timeout

在数据库连接字符串后面添加Connect Timeout=500(连接超时时间设为500秒)

    SqlConnection con = new SqlConnection("server=.;database=myDB;uid=sa;pwd=password;Connect Timeout=500")

运行查询后,不到30秒,仍然返回Timeout超时,问题未解决

设置Command对象属性CommandTimeout

SqlCommand cmd = new SqlCommand();
cmd.CommandTimeout = 180;

运行,可以解决Command的执行超时问题,这里设置的时间的180秒,可根据需要设置,如果过长,也可设置为0,设置为0时表示不限制时间,此属性值需慎用。还需要在Web.config配置文件中设置http请求运行时限间

<system.web>      
    <httpRuntime maxRequestLength="102400" executionTimeout="720" />
</system.web>

这里设置的为720秒,前面的属性maxRequestLength一般用于用户上传文件限制大小!默认一般为4096 KB (4 MB)。

设置 DataAdapter对象属性SelectCommand

SqlDataAdapter da = new SqlDataAdapter(strsqll, sqlconstr);
da.SelectCommand.CommandTimeout = 180;

运行,解决超时问题。该条设置对DataAdapter返回多条记录查询超时问题的解决效果立竿见影(Repeater绑定数据多适用次情况)。设置时间为180秒,根据需要可更改。

Asp.net中关于超时的设置

在web.config 里<system.web>节点添加以下代码:

<system.web>      
    <httpRuntime maxRequestLength="102400" executionTimeout="720" />
</system.web>

MSDN解释:    

httpRuntime是配置asp.Net http运行时设置,以确定如何处理对asp.Net应用程序的请求。       executionTimeout:表示允许执行请求的最大时间限制,单位为秒 maxRequestLength:指示 ASP.Net 支持的最大文件上载大小。该限制可用于防止因用户将大量文件传递到该服务器而导致的拒绝服务攻击。指定的大小以 KB 为单位。默认值为 4096 KB (4 MB)。

WebService请求超时时间的设置:

扩大代理类的超时限制,默认是90秒  ,即在调用方法前指定超时时间。      
YourWebService yws = new YourWebService(); yws.Timeout = 1200000; //20分钟,单位是毫秒     
如果将 Timeout 属性设置为 Timeout.Infinite,则指示该请求无超时。即使 XML Web services 客户端可以将 Timeout 属性设置为无超时,Web 服务器仍可以在服务器端使请求超时。

IIS中请求超时设置。  

IIS-网站-属性 连接超时时间 1200秒

原文:https://www.cnblogs.com/zlqblog/p/4930319.html

使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象

 

本文需要对C#里的LINQ、Lambda 表达式 、委托有一定了解。

在工作中,经常遇到需要对比两个集合的场景,如:

  1. 页面集合数据修改,需要保存到数据库
  2. 全量同步上游数据到本系统数据库

在这些场景中,需要识别出需要新增、更新、删除的数据,由于每次应用是,需要比较的对象类型不一致,因此写了个相对通用的方法。这个过程中,需要理解的有以下2个核心概念:

  1. 唯一标识比较: 如果两个对象的唯一标识相等,则认为这两个对象在业务上代表同一个东西(次要属性是否相等暂不考虑)。
  2. 实体比较:表示两个对象在业务是不是相等(唯一标识相等、次要属性相等)。

代码示例如下:

void Main()
{
    // 对比源集合
    var source = GenerateStudent(1, 10000, 1000);
    // 目标集合
    var target = GenerateStudent(5000, 10000, 1000);

    // 唯一标识比较
    Func<Student, Student, bool> keyCompartor = (s, t) => s.Id == t.Id;
    // 实体相等比较
    Func<Student, Student, bool> entityCompartor = (s, t) => s.Id == t.Id && s.Name.Equals(t.Name) && s.Age == t.Age;

    // 新增前准备
    Func<Student, Student> insertAction = (s) =>
    {
        return new Student
        {
            Id = s.Id,
            Name = s.Name,
            Age = s.Age,
            Operation = "Insert"
        };
    };

    // 更新前准备
    Func<Student, Student, Student> updateAction = (s, t) =>
    {
        t.Name = s.Name;
        t.Age = s.Age;
        t.Operation = "Update";

        return t;
    };

    // 删除前准备
    Func<Student, Student> deleteAction = (t) =>
    {
        t.Operation = "Delete";
        return t;
    };

    // 去掉相等对象
    RemoveDuplicate(source, target, entityCompartor, (s1, s2) => s1.Id == s2.Id, keyCompartor);

    // 需要新增的集合
    var insertingStudents = GetInsertingEntities(source, target, keyCompartor, insertAction);
    // 需要更新的集合
    var updatingStudents = GetUpdatingEntities(source, target, keyCompartor, entityCompartor, updateAction);
    // 需要删除的集合
    var deletingStudents = GetDeletingEntities(source, target, keyCompartor, deleteAction);

    // 后续业务
    // InsertStudents(insertingStudents);
    // UpdateStudents(updatingStudents);
    // DeleteStudents(deletingStudents);
}

// 集合去重
private void RemoveDuplicate<S, T>(List<S> source, List<T> target, Func<S, T, bool> entityCompartor,
    Func<S, S, bool> sourceKeyCompartor, Func<S, T, bool> keyComportor)
{
    var sameEntities = source.Where(s => target.Exists(t => entityCompartor(s, t))).ToList();
    source.RemoveAll(s => sameEntities.Exists(s2 => sourceKeyCompartor(s, s2)));
    target.RemoveAll(t => sameEntities.Exists(s => keyComportor(s, t)));
}

// 获取需要新增的对象集合
private List<T> GetInsertingEntities<S, T>(List<S> source, List<T> target, Func<S, T, bool> keyComportor,
    Func<S, T> insertAction)
{
    var result = new List<T>();
    foreach (var s in source)
    {
        var t = target.FirstOrDefault(x => keyComportor(s, x));
        if (t == null)
        {
            // 目标集合中不存在,则新增
            result.Add(insertAction(s));
        }
    }

    return result;
}

// 获取需要更新的对象集合
private List<T> GetUpdatingEntities<S, T>(List<S> source, List<T> target, Func<S, T, bool> keyComportor,
    Func<S, T, bool> entityCompartor, Func<S, T, T> updateAction)
{
    var result = new List<T>();
    foreach (var s in source)
    {
        var t = target.FirstOrDefault(x => keyComportor(s, x));
        if (t != null && !entityCompartor(s, t))
        {
            // 目标集合中存在,但是次要属性不相等,则更新
            result.Add(updateAction(s, t));
        }
    }

    return result;
}

// 获取需要删除的对象集合
private List<T> GetDeletingEntities<S, T>(List<S> source, List<T> target,
    Func<S, T, bool> keyComportor, Func<T, T> deleteAction)
{
    var result = new List<T>();
    foreach (var t in target)
    {
        var s = source.FirstOrDefault(x => keyComportor(x, t));
        if (s == null)
        {
            // 源集合中存在,目标集合中需要删除
            result.Add(deleteAction(t));
        }
    }

    return result;
}

// 随机生成测试集合
private List<Student> GenerateStudent(int minId, int maxId, int maxNumber)
{
    var r = new Random();
    var students = new List<Student>();
    for (int i = 0; i < maxNumber; i++)
    {
        students.Add(new Student
        {
            Id = r.Next(minId, maxId),
            Name = $"name: {r.Next(1, 10)}",
            Age = r.Next(6, 10)
        });
    }

    return students.GroupBy(s => s.Id).Select(s => s.First()).ToList();
}

public class Student
{
    public int Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

    public string Operation { get; set; }
}

例子中源集合与目标集合使用了相同的对象Student,但实际使用中,两者的类型可以不一样,只要最终返回目标集合的类型就可以了。

上面是我对集合比较的一点心得,只满足了小数据量的业务情景,并没有在大数据量的情况下做过调优。在这里也算是抛砖引玉,大家要是有更好的办法,还希望不吝赐教。

c# 制作正方形图片

 
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace treads
{
    /// <summary>
    /// 制作小正方形
    /// </summary>
    class Class3
    {
        private string srcFileName = @"x";//获取图片的路径
        private string srcFileName1 = @"x";//要保持图片的新路径

        /// <summary>
        /// 保存图片
        /// </summary>
        /// <param name="image">Image 对象</param>
        /// <param name="savePath">保存路径</param>
        /// <param name="ici">指定格式的编解码参数</param>
        private static void SaveImage(Image image, string savePath, ImageCodecInfo ici)
        {
            //设置 原图片 对象的 EncoderParameters 对象
            EncoderParameters parameters = new EncoderParameters(1);
            parameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, ((long)100));
            image.Save(savePath, ici, parameters);
            parameters.Dispose();
        }

        /// <summary>
        /// 获取图像编码解码器的所有相关信息
        /// </summary>
        /// <param name="mimeType">包含编码解码器的多用途网际邮件扩充协议 (MIME) 类型的字符串</param>
        /// <returns>返回图像编码解码器的所有相关信息</returns>
        private static ImageCodecInfo GetCodecInfo(string mimeType)
        {
            ImageCodecInfo[] CodecInfo = ImageCodecInfo.GetImageEncoders();
            foreach (ImageCodecInfo ici in CodecInfo)
            {
                if (ici.MimeType == mimeType)
                    return ici;
            }
            return null;
        }

        /// <summary>
        /// 计算新尺寸
        /// </summary>
        /// <param name="width">原始宽度</param>
        /// <param name="height">原始高度</param>
        /// <param name="maxWidth">最大新宽度</param>
        /// <param name="maxHeight">最大新高度</param>
        /// <returns></returns>
        private static Size ResizeImage(int width, int height, int maxWidth, int maxHeight)
        {
            //此次2012-02-05修改过=================
            if (maxWidth <= 0)
                maxWidth = width;
            if (maxHeight <= 0)
                maxHeight = height;
            //以上2012-02-05修改过=================
            decimal MAX_WIDTH = (decimal)maxWidth;
            decimal MAX_HEIGHT = (decimal)maxHeight;
            decimal ASPECT_RATIO = MAX_WIDTH / MAX_HEIGHT;

            int newWidth, newHeight;
            decimal originalWidth = (decimal)width;
            decimal originalHeight = (decimal)height;

            if (originalWidth > MAX_WIDTH || originalHeight > MAX_HEIGHT)
            {
                decimal factor;
                // determine the largest factor
                if (originalWidth / originalHeight > ASPECT_RATIO)
                {
                    factor = originalWidth / MAX_WIDTH;
                    newWidth = Convert.ToInt32(originalWidth / factor);
                    newHeight = Convert.ToInt32(originalHeight / factor);
                }
                else
                {
                    factor = originalHeight / MAX_HEIGHT;
                    newWidth = Convert.ToInt32(originalWidth / factor);
                    newHeight = Convert.ToInt32(originalHeight / factor);
                }
            }
            else
            {
                newWidth = width;
                newHeight = height;
            }
            return new Size(newWidth, newHeight);
        }

        /// <summary>
        /// 得到图片格式
        /// </summary>
        /// <param name="name">文件名称</param>
        /// <returns></returns>
        public static ImageFormat GetFormat(string name)
        {
            string ext = name.Substring(name.LastIndexOf(".") + 1);
            switch (ext.ToLower())
            {
                case "jpg":
                case "jpeg":
                    return ImageFormat.Jpeg;
                case "bmp":
                    return ImageFormat.Bmp;
                case "png":
                    return ImageFormat.Png;
                case "gif":
                    return ImageFormat.Gif;
                default:
                    return ImageFormat.Jpeg;
            }
        }

        /// <summary>
        /// 制作小正方形
        /// </summary>
        /// <param name="image">图片对象</param>
        /// <param name="newFileName">新地址</param>
        /// <param name="newSize">长度或宽度</param>
        public static void MakeSquareImage(Image image, string newFileName, int newSize)
        {
            int i = 0;
            int width = image.Width;
            int height = image.Height;
            if (width > height)
                i = height;
            else
                i = width;

            Bitmap b = new Bitmap(newSize, newSize);

            try
            {
                Graphics g = Graphics.FromImage(b);
                //设置高质量插值法
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                //设置高质量,低速度呈现平滑程度
                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.PixelOffsetMode = PixelOffsetMode.HighQuality;
                //清除整个绘图面并以透明背景色填充
                g.Clear(Color.Transparent);
                if (width < height)
                    g.DrawImage(image, new Rectangle(0, 0, newSize, newSize), new Rectangle(0, (height - width) / 2, width, width), GraphicsUnit.Pixel);
                else
                    g.DrawImage(image, new Rectangle(0, 0, newSize, newSize), new Rectangle((width - height) / 2, 0, height, height), GraphicsUnit.Pixel);

                SaveImage(b, newFileName, GetCodecInfo("image/" + GetFormat(newFileName).ToString().ToLower()));
            }
            finally
            {
                image.Dispose();
                b.Dispose();
            }
        }

        /// <summary>
        /// 制作小正方形
        /// </summary>
        /// <param name="fileName">图片文件名</param>
        /// <param name="newFileName">新地址</param>
        /// <param name="newSize">长度或宽度</param>
        public static void MakeSquareImage(string fileName,string newFileName, int newSize)
        {
            MakeSquareImage(Image.FromFile(fileName), newFileName, newSize);
        }
    }
}
c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

JavaScript 事件循环及异步原理(完全指北)

 


引言

最近面试被问到,JS 既然是单线程的,为什么可以执行异步操作?
当时脑子蒙了,思维一直被困在 单线程 这个问题上,一直在思考单线程为什么可以额外运行任务,其实在我很早以前写的博客里面有写相关的内容,只不过时间太长给忘了,所以要经常温习啊:(浅谈 Generator 和 Promise 的原理及实现)

  1. JS 是单线程的,只有一个主线程
  2. 函数内的代码从上到下顺序执行,遇到被调用的函数先进入被调用函数执行,待完成后继续执行
  3. 遇到异步事件,浏览器另开一个线程,主线程继续执行,待结果返回后,执行回调函数

其实 JS 这个语言是运行在宿主环境中,比如 浏览器环境nodeJs环境

  • 在浏览器中,浏览器负责提供这个额外的线程
  • 在 Node 中,Node.js 借助 libuv 来作为抽象封装层, 从而屏蔽不同操作系统的差异,Node可以借助libuv来实现多线程。

而这个异步线程又分为 微任务 和 宏任务,本篇文章就来探究一下 JS 的异步原理以及其事件循环机制

为什么 JavaScript 是单线程的

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这样设计的方案主要源于其语言特性,因为 JavaScript 是浏览器脚本语言,它可以操纵 DOM ,可以渲染动画,可以与用户进行互动,如果是多线程的话,执行顺序无法预知,而且操作以哪个线程为准也是个难题。

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

在 HTML5 时代,浏览器为了充分发挥 CPU 性能优势,允许 JavaScript 创建多个线程,但是即使能额外创建线程,这些子线程仍然是受到主线程控制,而且不得操作 DOM,类似于开辟一个线程来运算复杂性任务,运算好了通知主线程运算完毕,结果给你,这类似于异步的处理方式,所以本质上并没有改变 JavaScript 单线程的本质。

函数调用栈与任务队列

函数调用栈

JavaScript 只有一个主线程和一个调用栈(call stack),那什么是调用栈呢?

这类似于一个乒乓球桶,第一个放进去的乒乓球会最后一个拿出来。

举个栗子:

function a() {
  console.log("I'm a!");
};

function b() {
  a();
  console.log("I'm b!");
};

b();

执行过程如下所示:

  • 第一步,执行这个文件,此文件会被压入调用栈(例如此文件名为 main.js

    call stack
    main.js
  • 第二步,遇到 b() 语法,调用 b() 方法,此时调用栈会压入此方法进行调用:

    call stack
    b()
    main.js
  • 第三步:调用 b() 函数时,内部调用的 a() ,此时 a() 将压入调用栈:

    call stack
    a()
    b()
    main.js
  • 第四步:a() 调用完毕输出 I'm a!,调用栈将 a() 弹出,就变成如下:

    call stack
    b()
    main.js
  • 第五步:b()调用完毕输出I'm b!,调用栈将 b() 弹出,变成如下:

    call stack
    main.js
  • 第六步:main.js 这个文件执行完毕,调用栈将 b() 弹出,变成一个空栈,等待下一个任务执行:

    call stack
     

这就是一个简单的调用栈,在调用栈中,前一个函数在执行的时候,下面的函数全部需要等待前一个任务执行完毕,才能执行。

但是,有很多任务需要很长时间才能完成,如果一直都在等待的话,调用栈的效率极其低下,这时,JavaScript 语言设计者意识到,这些任务主线程根本不需要等待,只要将这些任务挂起,先运算后面的任务,等到执行完毕了,再回头将此任务进行下去,于是就有了 任务队列 的概念。

任务队列

所有任务可以分成两种,一种是 同步任务(synchronous),另一种是 异步任务(asynchronous) 。

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有 "任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

所以,当在执行过程中遇到一些类似于 setTimeout 等异步操作的时候,会交给浏览器的其他模块进行处理,当到达 setTimeout 指定的延时执行的时间之后,回调函数会放入到任务队列之中。

当然,一般不同的异步任务的回调函数会放入不同的任务队列之中。等到调用栈中所有任务执行完毕之后,接着去执行任务队列之中的回调函数。

用一张图来表示就是:

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

上图中,调用栈先进行顺序调用,一旦发现异步操作的时候就会交给浏览器内核的其他模块进行处理,对于 Chrome浏览器来说,这个模块就是 webcore 模块,上面提到的异步API,webcore 分别提供了 DOM Binding 、 networktimer 模块进行处理。等到这些模块处理完这些操作的时候将回调函数放入任务队列中,之后等栈中的任务执行完之后再去执行任务队列之中的回调函数。

我们先来看一个有意思的现象,我运行一段代码,大家觉得输出的顺序是什么:

  setTimeout(() => {
    console.log('setTimeout')
  }, 22)
  for (let i = 0; i++ < 2;) {
    i === 1 && console.log('1')
  }
  setTimeout(() => {
    console.log('set2')
  }, 20)
  for (let i = 0; i++ < 100000000;) {
    i === 99999999 && console.log('2')
  }

没错!结果很量子化:

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

那么这实际上是一个什么过程呢?那我就拿上面的一个过程解析一下:

  • 首先,文件入栈

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 开始执行文件,读取到第一行代码,当遇到 setTimeout 的时候,执行引擎将其添加到栈中。(由于字体太细我调粗了一点。。。)

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 调用栈发现 setTimeout 是 Webapis中的 API,因此将其交给浏览器的 timer 模块进行处理,同时处理下一个任务。

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第二个 setTimeout 入栈

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 同上所示,异步请求被放入 异步API 进行处理,同时进行下一个入栈操作:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 在进行异步的同时,app.js 文件调用完毕,弹出调用栈,异步执行完毕后,会将回调函数放入任务队列:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 任务队列通知调用栈,我这边有任务还没有执行,调用栈则会执行任务队列里的任务:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

上面的流程解释了浏览器遇到 setTimeout 之后究竟如何执行的,其实总结下来就是以下几点:

  1. 调用栈顺序调用任务
  2. 当调用栈发现异步任务时,将异步任务交给其他模块处理,自己继续进行下面的调用
  3. 异步执行完毕,异步模块将任务推入任务队列,并通知调用栈
  4. 调用栈在执行完当前任务后,将执行任务队列里的任务
  5. 调用栈执行完任务队列里的任务之后,继续执行其他任务

这一整个流程就叫做 事件循环(Event Loop)

那么,了解了这么多,小伙伴们能从事件循环上面来解析下面代码的输出吗?

  for (var i = 0; i < 10; i++) {
    setTimeout(() => {
      console.log(i)
    }, 1000)
  }
  console.log(i)

解析:

  • 首先由于 var 的变量提升,i 在全局作用域都有效
  • 再次,代码遇到 setTimeout 之后,将该函数交给其他模块处理,自己继续执行 console.log(i) ,由于变量提升,i 已经循环10次,此时 i 的值为 10 ,即,输出 10
  • 之后,异步模块处理好函数之后,将回调推入任务队列,并通知调用栈
  • 1秒之后,调用栈顺序执行回调函数,由于此时 i 已经变成 10 ,即输出10次 10

用下图示意:

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

现在小伙伴们是否已经恍然大悟,从底层了解了为什么这个代码会输出这个内容吧:

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

那么问题又来了,我们看下面的代码:

  setTimeout(() => {
    console.log(4)
  }, 0);
  new Promise((resolve) =>{
    console.log(1);
    for (var i = 0; i < 10000000; i++) {
      i === 9999999 && resolve();
    }
    console.log(2);
  }).then(() => {
    console.log(5);
  });
  console.log(3);

大家觉得这个输出是多少呢?

有小伙伴就开始分析了,promise 也是异步,先执行里面函数的内容,输出 1 和 2,然后执行下面的函数,输出 3 ,但 Promise 里面需要循环999万次,setTimeout 却是0毫秒执行,setTimeout 应该立即推入执行栈, Promise 后推入执行栈,结果应该是下图:

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

实际上答案是 1,2,3,5,4 噢,这是为什么呢?这就涉及到任务队列的内部,宏任务和微任务。

宏任务和微任务

什么是宏任务和微任务

任务队列又分为 macro-task(宏任务) 与 micro-task(微任务) ,在最新标准中,它们被分别称为 task 与 jobs 。

  • macro-task(宏任务)大概包括:script(整体代码)setTimeoutsetIntervalsetImmediate(NodeJs)I/OUI rendering
  • micro-task(微任务)大概包括: process.nextTick(NodeJs)PromiseObject.observe(已废弃)MutationObserver(html5新特性)
  • 来自不同任务源的任务会进入到不同的任务队列。其中 setTimeout 与 setInterval 是同源的。

事实上,事件循环决定了代码的执行顺序,从全局上下文进入函数调用栈开始,直到调用栈清空,然后执行所有的micro-task(微任务),当所有的micro-task(微任务)执行完毕之后,再执行macro-task(宏任务),其中一个macro-task(宏任务)的任务队列执行完毕(例如setTimeout 队列),再次执行所有的micro-task(微任务),一直循环直至执行完毕。

解析

现在我就开始解析上面的代码。

  • 第一步,整体代码 script 入栈,并执行 setTimeout 后,执行 Promise

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第二步,执行时遇到 Promise 实例,Promise 构造函数中的第一个参数,是在new的时候执行,因此不会进入任何其他的队列,而是直接在当前任务直接执行了,而后续的.then则会被分发到micro-taskPromise队列中去。

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第三步,调用栈继续执行宏任务 app.js,输出3并弹出调用栈,app.js 执行完毕弹出调用栈:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第四步,这时,macro-task(宏任务)中的 script 队列执行完毕,事件循环开始执行所有的 micro-task(微任务)

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第五步,调用栈发现所有的 micro-task(微任务) 都已经执行完毕,又跑去macro-task(宏任务)调用 setTimeout 队列:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第六步,macro-task(宏任务) setTimeout 队列执行完毕,调用栈又跑去微任务进行查找是否有未执行的微任务,发现没有就跑去宏任务执行下一个队列,发现宏任务也没有队列执行,此次调用结束,输出内容1,2,3,5,4

那么上面这个例子的输出结果就显而易见。大家可以自行尝试体会。

总结

  1. 不同的任务会放进不同的任务队列之中。
  2. 先执行macro-task,等到函数调用栈清空之后再执行所有在队列之中的micro-task
  3. 等到所有micro-task执行完之后再从macro-task中的一个任务队列开始执行,就这样一直循环。
  4. 宏任务和微任务的队列执行顺序排列如下:
  5. macro-task(宏任务)script(整体代码)setTimeoutsetIntervalsetImmediate(NodeJs)I/OUI rendering
  6. micro-task(微任务)process.nextTick(NodeJs)PromiseObject.observe(已废弃)MutationObserver(html5新特性)

进阶举例

那么,我再来一些有意思一点的代码:

<script>
  setTimeout(() => {
    console.log(4)
  }, 0);
  new Promise((resolve) => {
    console.log(1);
    for (var i = 0; i < 10000000; i++) {
      i === 9999999 && resolve();
    }
    console.log(2);
  }).then(() => {
    console.log(5);
  });
  console.log(3);
</script>
<script>
  console.log(6)
  new Promise((resolve) => {
    resolve()
  }).then(() => {
    console.log(7);
  });
</script>

这一段代码输出的顺序是什么呢?

其实,看明白上面流程的同学应该知道整个流程,为了防止一些同学不明白,我再简单分析一下:

  • 首先,script1 进入任务队列(为了方便起见,我把两块script 命名为script1script2):

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第二步,script1 进行调用并弹出调用栈:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第三步,script1执行完毕,调用栈清空后,直接调取所有微任务:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第四步,所有微任务执行完毕之后,调用栈会继续调用宏任务队列:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第五步,执行 script2,并弹出:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第六步,调用栈开始执行微任务:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

  • 第七步,调用栈调用完所有微任务,又跑去执行宏任务:

    c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

至此,所有任务执行完毕,输出 1,2,3,5,6,7,4

了解了上面的内容,我觉得再复杂一点异步调用关系你也能搞定:

setImmediate(() => {
    console.log(1);
},0);
setTimeout(() => {
    console.log(2);
},0);
new Promise((resolve) => {
    console.log(3);
    resolve();
    console.log(4);
}).then(() => {
    console.log(5);
});
console.log(6);
process.nextTick(()=> {
    console.log(7);
});
console.log(8);
//输出结果是3 4 6 8 7 5 2 1

c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)

终极测试

setTimeout(() => {
    console.log('to1');
    process.nextTick(() => {
        console.log('to1_nT');
    })
    new Promise((resolve) => {
        console.log('to1_p');
        setTimeout(() => {
          console.log('to1_p_to')
        })
        resolve();
    }).then(() => {
        console.log('to1_then')
    })
})

setImmediate(() => {
    console.log('imm1');
    process.nextTick(() => {
        console.log('imm1_nT');
    })
    new Promise((resolve) => {
        console.log('imm1_p');
        resolve();
    }).then(() => {
        console.log('imm1_then')
    })
})

process.nextTick(() => {
    console.log('nT1');
})
new Promise((resolve) => {
    console.log('p1');
    resolve();
}).then(() => {
    console.log('then1')
})

setTimeout(() => {
    console.log('to2');
    process.nextTick(() => {
        console.log('to2_nT');
    })
    new Promise((resolve) => {
        console.log('to2_p');
        resolve();
    }).then(() => {
        console.log('to2_then')
    })
})

process.nextTick(() => {
    console.log('nT2');
})

new Promise((resolve) => {
    console.log('p2');
    resolve();
}).then(() => {
    console.log('then2')
})

setImmediate(() => {
    console.log('imm2');
    process.nextTick(() => {
        console.log('imm2_nT');
    })
    new Promise((resolve) => {
        console.log('imm2_p');
        resolve();
    }).then(() => {
        console.log('imm2_then')
    })
})
// 输出结果是:?

大家可以在评论里留言结果哟~