.NET基础拾遗(8)ADO.NET与数据库开发基础

1.1 ADO.NET支持哪几种数据源?

  ① System.Data.SqlClient

  .NET程序员最常用的了。通过OLEDB或者ODBC都可以访问,但是SqlClient下的组件直接针对MSSQL,因此ADO.NET是为其专门做了一些优化工作。

  ② System.Data.OracleClient

  针对Oracle数据库产品且得搭配Oracle数据库的客户端组件(Oracle.DataAccess.dll)一起使用。

  ③ System.Data.OleDb

  该命名空间下的组件主要针对OLEDB(Microsoft提供的通向不同数据源的低级API)的标准接口,它还可以连接其他非SQL数据类型的数据源。OLEDB是一种标准的接口,实现了不同数据源统一接口的功能。

  ④ System.Data.Odbc

  该命名空间下的组件针对ODBC标准接口。

总体来说,ADO.NET为我们屏蔽了所有的数据库访问层次,提供了统一的API给我们,使我们无需考虑底层的数据源是具体的DataBase还是另一种标准接口。

下图直观地展示了ADO.NET与可能的数据源的连接:

二、ADO.NET和数据库的连接

2.1 简述数据库连接池

  数据库连接一般都被认为是一个性能成本相对较大的动作。

  (1)数据库连接池的基本概念

  一个存储数据库连接的缓冲池,由于连接和断开一个数据库的开销很大(经典的TCP三次握手和四次挥手),反复连接和断开数据库对于系统的性能影响将会非常严重。

  当一个用户新申请了一个数据库连接时,当数据库池内连接匹配的情况下,用户会从连接池中直接获得一个被保持的连接。在用户使用完调用Close关闭连接时,连接池会将该连接返回到活动连接池中,而不是真正关闭连接。连接回到了活动链接池中后,即可在下一个Open调用中重复使用。

默认情况下,数据库连接时处于启用状态的。我们也可以通过数据库连接字符串设置关闭数据库连接池.

    (2)数据库连接的复用

   由于数据源和连接参数选择的不同,每个数据库的连接并不是完全通用的。因此,ADO.NET选择通过连接字符串来区分。一旦用户使用某个连接字符串来申请数据库连接,ADO.NET将判断连接池中是否存在拥有相同连接字符串的连接,如果有则直接分配,没有则新建连接。

     (3)不同数据源的连接池机制

  ADO.NET组件并不直接包含连接池,而针对不同类别的数据源指定不同的连接池方案。对于SqlClient、OracleClient命名空间下的组件,使用的连接池是由托管代码直接编写的,可理解为连接池直接在.NET框架中运行。而对于OLEDB和ODBC的数据源来说,连接池的实现完全依靠OLEDB和ODBC提供商实现,ADO.NET只与其约定相应规范。

2.2 如何提高连接池内连接的重用率

  (1)连接池重用率低下的原因

  由于数据库连接池仅按照数据库连接字符串来判断连接是否可重用,所以连接字符串内的任何改动都会导致连接失效。数据库连接字符串中最常被修改的两个属性就是数据库名和用户名/密码。因此,对于多数据库的系统来说,只有同一数据库的连接才会被共用.

      (2)如何提高数据库连接池重用率

      这里提供一种能够有效提高数据库连接池重用率的方式,但是也会带来一点小安全隐患,在进行设计时需要权衡利弊关系,并根据实际情况来指定措施。

  ① 建立跳板数据库

  在数据库内建立一个所有权限用户都能访问的跳板数据库,在进行数据库连接时先连接到该数据库,然后再使用 use databasename 这样的SQL语句来选择需要访问的数据库,这样就能够避免因为访问的数据库不一致而导致连接字符串不一致的情况。

  下面的示例代码演示了这一做法:

// 假设这里使用Entry数据作为跳板数据库,然后再使用databaseName指定的数据库
    using (SqlConnection connection = new SqlConnection("Server=192.168.80.100;Uid=public;Pwd=public;Database=Entry"))
    {
        connection.Open();
        SqlCommand command = connection.CreateCommand();
        command.CommandText = string.Format("USE {0}", databaseName);
        command.ExecuteNonQuery();
    }

    ② 不使用数据库用户系统来管理系统权限

  这样做的结果就是永远使用管理员的账号来连接数据库,而在做具体工作时再根据用户的实际权限,使用代码来限定操作。

三、使用ADO.NET读写数据库

3.1 ADO.NET支持访问数据库的方式有哪些?

  一种是连接式的访问模式,而另外一种则是离线式的访问模式。

  (1)连接式的访问

  连接式的访问是指读取数据时保持和数据库的连接且在使用时独占整个连接,逐步读取数据。这种模式比较适合从数据量庞大的数据库中查询数据且不能确定读取数量的情况。使用XXXCommand和XXXDataReader对象来读取数据就是一个典型的连接式数据访问,这种模式的缺点就是:数据库连接被长时间地保持在打开的状态。

     (2)脱机式的访问

  脱机式的访问并不是指不连接数据库,而是指一般在读取实际数据时连接就已经断开了。脱机式访问方式在连接至数据库后,会根据SQL命令批量读入所有记录,这样就能直接断开数据库连接以供其他线程使用,读入的记录将暂时存放在内存之中。脱机式访问的优点就在于不会长期占用数据库连接资源,而这样做的代价就是将消耗内存来存储数据,在大数据量查询的情况下该方式并不适用。例如,使用XXXDataAdapter和DataSet对象就是最常用的脱机式访问方式。

3.2 简述SqlDataAdapter的基本工作机制

  一个SqlDataAdapter对象,在数据库操作中充当了中间适配的角色,组织起数据缓存对数据库的所有操作,进行统一执行。一个SqlDataAdapter对象内实际包含四个负责具体操作的SqlCommand对象,它们分别负责查询、更新、插入和删除操作。

  如上图所示,实际上进行数据操作的是包含在SqlDataAdapter内的四个SqlCommand对象,而当SqlDataAdapter的Update方法被调用时,它会根据DataSet独享的更新情况而调用插入、删除和更新等命令。

3.3 如何实现批量更新的功能?

  (1)批量更新的概念

  使用XXXDataAdapter更新数据,由于每一行都需要都需要一个从程序集到数据库的往返,在大批量更新的情况下,效率是非常低的。可以考虑使用一次发送多条更新命令的处理方式,这就需要用到UpdateBatchSize属性。在.NET 2.0之后,SqlClient和OracleClient都支持这个属性,这里以SQL Server数据源为例,介绍一下UpdateBatchSize的基本使用。

UpdateBatchSize的值一共有三种:

  ① =0,DbDataAdapter将使用服务器能处理的最大批处理大小;

  ② =1,禁用批量更新;

  ③ >1,使用UpdateBatchSize操作批处理一次性发送的量;

     当批量更新被允许时,SqlDataAdapter的Update方法将每次发送多条更新命令到数据库,从而提高性能。

  使用批量更新并不意味着SQL的合并或优化。批量的意义在于把多个发往数据库服务器的SQL语句放在一个请求中发送。例如,将UpdateBatchSize设置为20时,原本每个更新行发送一次更新命令将变为每20个更新行发送一次更新命令,而每个命令中包含了20个更新一行的命令。

public class DataHelper
    {
        private static readonly string conn_string = "Server=localhost;Integrated Security=true;database=TestDB";
        //选择、更新、删除和插入的SQL命令
        static readonly string SQL_SELECT = "SELECT * FROM DeptMaterialDetails";
        static readonly string SQL_UPDATE = "UPDATE DeptMaterialDetails SET Department=@Department,Item=@Item,Number=@Number where Id=@Id";
        static readonly string SQL_DELETE = "DELETE FROM DeptMaterialDetails where Id=@Id";
        static readonly string SQL_INSERT = "Insert INTO DeptMaterialDetails (Department,Item,Number) VALUES (@Department,@Item,@Number)";

        /// <summary>
        /// 得到SqlDataAdapter,私有方法
        /// </summary>
        /// <param name="con"></param>
        /// <returns></returns>
        private static SqlDataAdapter GetDataAdapter(SqlConnection con)
        {
            SqlDataAdapter sda = new SqlDataAdapter();
            sda.SelectCommand = new SqlCommand(SQL_SELECT, con);
            sda.UpdateCommand = new SqlCommand(SQL_UPDATE, con);
            sda.DeleteCommand = new SqlCommand(SQL_DELETE, con);
            sda.InsertCommand = new SqlCommand(SQL_INSERT, con);
            sda.UpdateCommand.Parameters.AddRange(GetUpdatePars());
            sda.InsertCommand.Parameters.AddRange(GetInsertPars());
            sda.DeleteCommand.Parameters.AddRange(GetDeletePars());
            return sda;
        }

        // 三个SqlCommand的参数
        private static SqlParameter[] GetInsertPars()
        {
            SqlParameter[] pars = new SqlParameter[3];
            pars[0] = new SqlParameter("@Department", SqlDbType.VarChar, 50, "Department");
            pars[1] = new SqlParameter("@Item", SqlDbType.VarChar, 50, "Item");
            pars[2] = new SqlParameter("@Number", SqlDbType.Int, 4, "Number");
            return pars;
        }

        private static SqlParameter[] GetUpdatePars()
        {
            SqlParameter[] pars = new SqlParameter[4];
            pars[0] = new SqlParameter("@Id", SqlDbType.VarChar, 50, "Id");
            pars[1] = new SqlParameter("@Department", SqlDbType.VarChar, 50, "Department");
            pars[2] = new SqlParameter("@Item", SqlDbType.VarChar, 50, "Item");
            pars[3] = new SqlParameter("@Number", SqlDbType.Int, 4, "Number");
            return pars;
        }

        private static SqlParameter[] GetDeletePars()
        {
            SqlParameter[] pars = new SqlParameter[1];
            pars[0] = new SqlParameter("@Id", SqlDbType.VarChar, 50, "Id");
            return pars;
        }

        /// <summary>
        /// 更新数据库,使用批量更新
        /// </summary>
        /// <param name="ds">数据集</param>
        public static void Update(DataSet ds)
        {
            using (SqlConnection connection = new SqlConnection(conn_string))
            {
                connection.Open();
                using (SqlDataAdapter adapater = GetDataAdapter(connection))
                {
                    // 设置批量更新
                    adapater.UpdateBatchSize = 0;
                    adapater.Update(ds);
                }
            }
        }
    }

  

原文地址:https://www.cnblogs.com/tiantianle/p/5932392.html