本分类以最简单的ADO.NET为基础,通过一步步升级完善,形成自研的ORM框架。针对每小章所运用的技术,会在文章开头有大概描述。

平台环境: VS2022   基于.NET 6


ADO.NET   

 ADO.NET的名称起源于ADO(ActiveX Data Objects),是一个COM组件库,用于在以往的Microsoft技术中访问数据。之所以使用ADO.NET名称,是因为Microsoft希望表明,这是在NET编程环境中优先使用的数据访问接口。关系型数据库而言—只认识Sql语句

对于ADO.NET而言,如下步骤就是需要完成的事

1 连接数据库

2 传递Sql语句

3 数据库执行Sql语句

4 返回执行结果 (返回受影响的行数(增删改)或返回结果集(查询))    

 

ADO.NET组成
SqlConnection(数据库连接器)
SqlCommand(数据库命名对象)
SqlCommandBuilder(生存SQL命令)
SqlDataReader(数据读取器)
SqlDataAdapter(数据适配器填充DataSet )
SqlParameter(为存储过程定义参数)
SqlTransaction(数据库事物)

 

 

ADO.NET的常置对象以及作用


Connection连接对象
Connection对象也称为数据库连接对象,Connection对象的功能是负责对数
据源的连接。所有Connection对象的基类都是DbConnection类
通过Connection对象+用户名和密码就可以链接到数据库
用户名+密码(数据库连接字符串)

Command对象
Command对象也称为数据库命令对象,Command对象主要执行包括添加、
删除、修改及查询数据的操作的命令。也可以用来执行存储过程。用于执行
存储过程时需要将Command对象的CommandType 属性设置为
CommandType.StoredProcedure,默认情况下CommandType 属性为
CommandType.Text,表示执行的是普通SQL语句。

ExecuteNonQuery
受影响的行数

ExecuteScalar
这个方法是针对SQL语句执行的结果是一行一列的结果集,这个方法只返回
查询结果集的第一行第一列。如聚合函数(count,max,min,agv,sum)。

ExecuteReader查询结果集,返回一个返回一个阅读器
SqlDataAdapter 

 

Sql注入和防止
SQL Injection:就是通过把SQL命令插入到Web表单递交或输入域名或页面
请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
防止注入的方式:参数化

 

ADO.NET常用对象简易示例

static  string connectionString = "Data Source=localhost;Initial Catalog=AdvancedCustomerDB;Persist Security Info=True;User ID=sa;Password=tiger;Encrypt=True;TrustServerCertificate=True;";

        /// <summary>
        /// 增删改,只是执行的SQL语句不一样而已
        /// 本次只做最基础的
        /// </summary>
        /// <returns></returns>
        public static int IntResult()
        {
            int result = 0;
            using (SqlConnection sqlConnection = new SqlConnection(connectionString))
            {

                sqlConnection.Open();
                string sql = $@"INSERT INTO [dbo].[Commodity]
                                               ([ProductId]
                                               ,[CategoryId]
                                               ,[Title]
                                               ,[Price]
                                               ,[Url]
                                               ,[ImageUrl])
                                         VALUES
                                               (@ProductId
                                               ,@CategoryId
                                               ,@Title
                                               ,@Price
                                               ,@Url
                                               ,@ImageUrl) ";

                //SqlCommand cmd = sqlConnection.CreateCommand();  //必须要基于SqlConnection来操作
                //cmd.CommandText = sql;

                //当然这里也可以采用如下方式做
                SqlCommand cmd = new SqlCommand(sql,sqlConnection);

                //这里使用了参数化,目的是为了防止SQL注入写入恶意SQL语句
                cmd.Parameters.Add(new SqlParameter("@CategoryId", 20088));
                cmd.Parameters.Add(new SqlParameter("@ProductId", 20088));
                cmd.Parameters.Add(new SqlParameter("@Title", "测试数据呀"));
                cmd.Parameters.Add(new SqlParameter("@Price", 123));
                cmd.Parameters.Add(new SqlParameter("@Url", "20088"));
                cmd.Parameters.Add(new SqlParameter("@ImageUrl", "ImageUrl"));


                result = cmd.ExecuteNonQuery();//受影响的行数 
            }


            return  result ;
        }

        public static DataTable SelectByDataTable()
        {
            DataTable result = new DataTable();

            using (SqlConnection sqlConnection = new SqlConnection(connectionString))
            {
                sqlConnection.Open();
                SqlCommand cmd = sqlConnection.CreateCommand();
                string sql = @" SELECT TOP (1000) [Id]
                     ,[ProductId]
                     ,[CategoryId]
                     ,[Title]
                      ,[Price]
                      ,[Url]
                      ,[ImageUrl]
                     FROM [AdvancedCustomerDB].[dbo].[Commodity]";

                cmd.CommandText = sql;
                SqlDataAdapter adapter = new SqlDataAdapter(cmd);

                ////DataSet ds = new DataSet();
                ////adapter.Fill(ds, "myds");

                DataTable dt = new DataTable();
                adapter.Fill(result);

            }

             return result;
        }

 

 

 

DataSet & DataTable & DataRow关系如下所示:
  DataTable = DataSet.Tables["TName"];
  DataRow = DataTable.Rows;
  string strFirstName = DataRow[0]["FirstName"];

ADO.NET中的事务

对于业务上来说,可能同时去操作多次数据库表
要成功--必须要都得成功
ACID
原子性
一致性 --- 要么都成功 只要有一个失败了,都失败了
隔离性
持久性

语法

SqlTransaction transaction = connection.BeginTransaction()

都成功:  transaction.Commit();

只要有一个失败:  在catch里  transaction.Rollback(); //回滚  可以把之前的操作 全部作废

using (SqlTransaction transaction = connection.BeginTransaction())
            {
                try
                {
                    //对于业务上来说,可能同时去操作多次数据库表
                    //要成功--必须要都得成功
                    //ACID
                    //原子性
                    //一致性 --- 要么都成功 只要有一个失败了,都失败了
                    //隔离性
                    //持久性 
                    #region 第一个操作
                    { 
                        string sql = @"Delete [AdvancedCustomerDB].[dbo].[Commodity] where id >@id";
                        SqlCommand cmd = connection.CreateCommand();
                        cmd.Transaction = transaction;
                        cmd.CommandText = sql;
                        cmd.CommandType = System.Data.CommandType.Text;
                        cmd.Parameters.Add(new SqlParameter("@id", 31010));
                        object deleteResult = cmd.ExecuteNonQuery();
                    }
                    #endregion 
                    #region 第二个操作
                    {
                        string sql = @"INSERT INTO [dbo].[Commodity]
                                                   ([ProductId]
                                                   ,[CategoryId]
                                                   ,[Title]
                                                   ,[Price]
                                                   ,[Url]
                                                   ,[ImageUrl])
                                             VALUES
                                                   (@ProductId
                                                   ,@CategoryId
                                                   ,@Title
                                                   ,@Price
                                                   ,@Url
                                                   ,@ImageUrl)";
                        SqlCommand cmd = connection.CreateCommand();
                        cmd.Transaction = transaction;
                        cmd.CommandText = sql;
                        cmd.CommandType = System.Data.CommandType.Text;
                        cmd.Parameters.Add(new SqlParameter("@ProductId", "12345798"));
                        cmd.Parameters.Add(new SqlParameter("@CategoryId", "234"));
                        cmd.Parameters.Add(new SqlParameter("@Title", "阿迪斯发的历史房间里大理石的减肥啦大家啊收到了房间阿里山大家否为切入且IP人的权威日u去啊收到了付款就去热哦卧铺去微软啊女权温柔请问u燃烧的拉法基而且认为犬瘟热委屈人去品味如却认为让评委u人千万人谱曲评委如请问u让排气污染确认文件请问人情味u机器人秋浦请问仆人七五二七文凭权威日平均气温聘请为譬如前往平壤请问热咖啡垃圾啊轻柔陪人犬瘟热九零七五七五阿松大的时间七二万人建立起文件全文请问日剧求问加入进来安德森咯i确认乔蛟翁人类九七七二为其让人犬瘟热零七九五去微软ui七二九六啊林则徐才能回家温柔i秋季哦文人风骨他"));
                        cmd.Parameters.Add(new SqlParameter("@Price", "4799.00"));
                        cmd.Parameters.Add(new SqlParameter("@Url", "zhaoxiedu.net"));
                        cmd.Parameters.Add(new SqlParameter("@ImageUrl", "zhaoxiedu.net"));
                        object insertResult = cmd.ExecuteNonQuery();
                    }
                    #endregion
                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    transaction.Rollback(); //回滚  可以把之前的操作 全部作废
                    Console.WriteLine(ex.Message);
                } 
            };

 

 

O/RM
ORM----操作数据库---对象关系映射
ORM(Object Relational Mapping)框架采用元数据来描述对象与关
系映射的细节。只要提供了持久化类与表的映射关系,ORM框架在运
行时就能参照映射文件的信息,把对象持久化到数据库中。
只关心对象了,不关注什么命令(Sql语句)---需要做任何操作都以对
象的思想去操作—通过映射---操作对象---落实到数据库中去;
底层实现一定还是要执行SQL语句--- 底层—还是ADO.NET ,ORM框
架基于ADO.NET的上层封装

 

ORM设计思想(伟大的思想)
1 以面向对象的思想来完成对于数据库的操作!
2 万物皆对象

ORM底层逻辑
1 数据库必然是只认识Sql语句
2 ORM的底层必然是ADO.NET
3 ORM也可以说是ADO的一种封装
ORM:
1 通过实体生成Sql语句—大量的反射
2 对应映射关系

 这里以笔者曾使用过的Nhibernate为例,

  调用NHibernate的DataAccessFactory类里CreateConnectTemplate方法,
在CreateConnectTemplate方法里有两个参数,第一个参数连接类型( SessionFactorySingle、SessionFactoryList、ADOTemplate),
第二个参数对于第一个参数的类型(0,1,2),在底层(需要借助反编译工具查看)会对这个方法的两个参数进行判断,如果两个参数都满足条件,调用ADOTemplateHelper类里的GetADOTemplateHelper方法,这个方法会读取Config里的connection子节点的配置信息返回ADOTemplateHelper类型,
调用ADOTemplateHelper类的GetPageDataList方法建立连接并进行分页处理

 

ADO VS ORM
ADO:
1 大量的Sql语句—业务不同,Sql语句不同
2 需要根据不同的场景编写不同Sql语句—灵活去编写Sql语句—提前优化Sql
语句—提供高性能的Sql语句
3 不适合快速开发
4 可编程性—更加灵活(对于高级开发,全方位发展的)
5 高性能—原生---接近于底层
ORM:
1 上手快,技术可以更加单一
2 不用关注数据库,不关注Sql语句,降低了开发成本
3 关注对象,以对象为核心
4 适合快速开发构建—提供更多的功能—代码生成器
5 性能有争议
6 生成的Sql语句—相对僵化– ORM(通用性)
ORM性能争议
1 二次封装---业务的执行,步骤多一些
2 映射的过程---必然从类到Sql语句变化---类---Sql语句---必然会有大量的反
射(损耗性能)
3 Sql语句僵化---数据库执行有性能损耗
部分是可以解决,引入缓存