zoukankan      html  css  js  c++  java
  • ado.net笔记

    1. ADO.Net基础
    • 程序要和数据库交互要通过ADO.Net进行,通过ADO.Net就能在程序中执行SQL语句了。ADO.Net中提供了对各种不同数据库的统一操作接口。
    • 直接在项目中内嵌mdf文件的方式使用SQLServer数据库(基于服务的数据库)。mdf文件随着项目走,用起来方便,和在数据库服务器上创建数据库没有什么区别,运行的时候会自动附加(Attach)。
    • 双击mdf文件会在“服务器资源管理器”中打开,管理方式和在Management Studio没有什么本质不同。要拷贝mdf文件需要关闭所有指向mdf文件的连接。
    • 正式生产运行的时候附加到SQLServer上、修改连接字符串即可,除此之外没有任何的区别,在“数据库”节点上点右键“附加”:在数据库点上→任务→分离就可以得到可以拷来的拷去的mdf文件。
    • 用的时候要在控制台、WinForm项目中在Main函数最开始的位置加入备注的代码。ASP.Net项目中不需要。
    1. 连接SQLServer
    • 连接字符串:程序通过连接字符串指定要连哪台服务器上的、哪个实例的哪个数据库、用什么用户名密码等。
    • 项目内嵌mdf文件形式的连接字符串

    “DataSource =.SQLEXPRESS:AttachDBFilename=|DataDirectory|Database1.mdf;Integrated Security=True; User Instance=True”。其中”.SQLEXPRESS”表示本机上的SQLEXPRESS实例,如果数据库实例名不是SQLEXPRESS,则需要修改。”Database1.mdf”为mdf的文件名。

         //使用using,那么conn只能在using的{}内使用,之外就会释放。当然也可以使用try catch语句。

                using(

                    SqlConnection conn = new SqlConnection(

                        @"Data Source=.SQLEXPRESS;

                        AttachDBFilename = |DataDirectory|Database1.mdf;

                        Integrated Security = True;

                        User Instance = True"

                        )           

                ){

                    conn.Open();

                }

         必有的代码:

         //加上下面的代码,可以去看数据库中数据的更新情况,否则,即使更新了数据库也是不显示的。

                string dataDir = AppDomain.CurrentDomain.BaseDirectory;

                if(dataDir.EndsWith(@"inDebug") ||

                    dataDir.EndsWith(@"inRelease"))

                {

                    dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;

                    AppDomain.CurrentDomain.SetData("DataDirectory",dataDir);

                }

    • ADO.Net中通过SqlConnection类创建到SQLServer的连接,SqlConnection代表一个数据库连接,ADO.Net中的连接等资源都实现了Idisposable接口,可以使用using进行资源管理,执行备注中的代码如果成功了就ok。
    • 可能出现的问题:连接不上数据库:连接字符串有错误。
    1. 执行简单的Insert语句
    • SqlCommand表示向服务器提交一个命令(SQL语句等),CommandText属性为要执行的SQL语句,ExecuteNonQuery方法执行一个非查询语句(Update、Insert、Delete等)
      using(SqlCommand cmd = conn.CreateCommand())
      {
          cmd.CommandText = “Insert into T_Users(UserName,Password) values (‘admin’,’888888’)”;
          cmd.ExecuteNonQuery();
      }
    • ExecuteNonQuery返回值是执行的影响行数。
    • 注意:
          string username = ‘test’;
          ……
          cmd.CommandText = “Insert into T_Users(UserName,Password) values(username,’888888’)”;
    1. ExecuteScalar
    • SqlCommand的ExecuteScalar方法用于执行查询,并返回查询所返回的结果集中的第一行的第一列,因为不能确定返回值的类型,所以返回值是object类型。

    l   cmd.CommandText = “select count(*) from T_Users”;

    l   int i = Convert.ToInt32(cmd.ExecuteScala());

    l   cmd.CommandText = “select getdate()”;

    l   DateTime dt = Convert.ToDataTime(cmd.ExecuteScalar());

    • 得到自动增长字段的主键值,在values关键词前加上output inserted.Id,其中Id为主键字段名。执行结果就是插入的主键值,用ExecuteScalar执行最方便。

    l   cmd.CommandText = “Insert into T_Users(UserName,Password) output inserted.Id values(‘admin’,’888888’);

    l   int i = Convert.ToInt32(cmd.ExecuteScalar());

    1. 执行查询
    • 执行有多行结果集的用ExecuteReader

    l   SqlDataReader reader = cmd.ExecuteReader();

    l   while(reader.Read())

    l   {

    Console.WriteLine(reader.GetString(1));

    l   }

    • reader的GetString、GetInt32等方法只接受整数参数,也就是序号,用GetOrdinal方法根据列名动态得到序号。
    • using与close与dispose。如果没有用using,只是close那么还可以open,而dispose就是直接销毁了,不能在open。使用using,当变量出了using区域,就会看有没有close,如果没有,先执行close,再执行dispose。使用using比较好,如果使用别的,可能不会执行到dispose语句。造成别的时候连接不上了。
    • 另一种写法:

         reader.GetString(reader.GetOrdinal("UserId"));  == reader.GetString(0)

         reader.GetString(reader.GetOrdinal("UserName"));  == reader.GetString(1)

         reader.GetString(reader.GetOrdinal("Password"));  == reader.GetString(2)

    1. SQL注入漏洞攻击
    • 登录判断:cmd.CommandText = "select count(*) from Users where UserName = '"+ userName + "' and Password = '" + password +"'"; 通过将参数拼到SQL语句中。
    • 构造恶意的Password: 12’ or ‘1’ = ‘1 这样的话,就会成功登录了。
    • 防范注入漏洞攻击的方法:不使用SQL语句拼接,通过参数赋值。
    1. 查询参数
    • SQL语句使用@UserName表示“此处用参数代替”,向SqlCommand的Parameters中添加参数cmd.CommandText = "select count(*) from Users where UserName = @UN and Password = @P";

             cmd.Parameters.Add(new SqlParameter("UN", userName));

             cmd.Parameters.Add(new SqlParameter("P", password));

    • 参数在SQLServer内部不是简单的字符串替换。SQLServer直接用添加的值进行比较,因此不会有注入漏洞攻击。
    1. 案例
    • 用户界面中进行用户名的增加。输错三次禁止登录,用数据库记录ErrorTimes。

    l   注意:在同一个连接中,如果SqlDataReader没有关闭,那么不能执行update类型的语句。因为指针还在指着它,是不能被别人访问的。可以再写一个函数,把它放在函数中。

    • 数据导入:从文本文件导入用户信息。易错点:Parameter的重复添加。

    l   连接数据库用创建SQL命令,执行一次就可以了。不要写在循环里面。

    l   有关乱码问题:使用nvarchar而不是varchar,插入的汉字之前加N

    • 数据导出:将用户信息导出到文本文件。
    • 问题:在导入汉字的过程中,如何保存使之在数据库中存入的不是乱码?

             设置读入时候的字符编码为Default即可。默认就是UTF-8的。

    1. 读取配置中的字符串
    • 将字符串写在代码中缺点:多次重复。

    l   添加App.config文件。App.config文件是.Net的通用配置文件,在ASP.Net中也能同样使用。

    l   在App.config中添加connectionString段,添加一个add项,用name属性起一个名字(比如DbConnStr),connectionString属性指定连接字符串。

    l   在“引用”节点上点右键“添加引用”,找到System.configuration。不是所有.Net中的类都能直接调用,类所在的Assembly要被添加到项目的引用中才可以。

    l   ConfigurationManager.ConnectionStrings[“DbConnStr”].ConnectionString得到连接字符串。

    l   如何改变了位置,只需在App.config文件中将连接字符串改变即可。

    1. DataSet
    • SqlDataReader是连接相关的,SqlDataReader中的查询结果并不是放到程序中的,而是放在数据库服务器中,SqlDataReader只是相当于放了一个指针(游标),只能读取当前游标指向的行,一旦连接断开就不能再读取,这样做的好处就是无论查询结果有多少条,对程序占用的内存几乎没有影响。
    • SqlDataReader对于小数据量的数据来说带来的只能麻烦,优点可以忽略不计。ADO.Net中提供了数据集的机制,将查询结果填充到本地内存中,这样连接断开、服务器断开都不影响数据的读取。
    • DataSet dataset = new DataSet();
    • SqlDataAdpater adpater = new SqlDataAdpater(cmd);
    • Adpter.Fill(dataset);
    • SqlDataAdpater是DataSet和数据库之间沟通的桥梁。数据集DataSet包含若干表DataTable,DataTable包含若干行DataRow,foreach(DataRow row in dataset.Tables[0].Rows) row[“Name”]
    1. DataSet的更新
    • 可以更新行row[“Name”]=”changed”、删除行datatable.Rows.Remove()、新增行datatable.NewRow()。这一切都是修改的内存中的DataSet,没有修改数据库中的。
    • 可以调用SqlDataAdapter的Update方法将对DataSet的修改提交到数据库,Update方法有很多重载方法,可以提交整个DataSet、DataTable或者若干DataRow。但是需要为SqlDataAdapter提供DeleteAdpater提供DeleteCommand、UpdateCommand、InsertCommand它才知道如何将对DataSet的修改提交到数据库,由于这几个Command要求的格式非常的苛刻,因此开发人员自己写非常的困难,可以用SqlCommandBuilder自动生成这几个Command,用法简单,new SqlCommandBuiler(adapter)。查看生成的Command。SqlCommandBuilder要求表必须有主键。
    • 通过DataRow的RowState可以获得行的状态(删除、修改、新增等);
    • 调用DataSet的GetChangeds()方法得到变化的结果集,降低传递的资源占用。
    1. 可空类型
    • Int? 可空的int,触决数据库和对int不能为空的不同设置的问题
    • Int? i3 = 4; int i4 = i3.Value;
    1. 弱类型DataSet的缺点 
    • 只能通过列名引用,dataset.Tables[0].Rows[0][“Age”],如果写错了列名编译时不会发现错误,因此开发时必须要记着列名。
    • Int age = Convert.ToInt32(dataset.Rows[0][“Age”]),取到的字段的值是object类型,必须小心翼翼的进行类型转换。
    • 将DataSet传递给其他使用者,使用者很难识别出有哪些列可以使用。
    • 运行时才能知道所有列名,数据绑定麻烦,无法使用Winform、ASP.Net的快速开发功能。
    • 自己动手写强类型DataSet(类型化DataSet,TypeDataSet),创建继承自DataSet的PersonDataSet类,封装出int?Age等属性和bool IsAgeNull等方法,向PersonDataSet中填充。
    1. VS自动生成强类型DataSet
    • 添加→新建项→数据集
    • 将表从服务器资源管理器拖放到DataSet中。注意拖放过程是自动根据表结构生成强类型DataSet等类,没有把数据也拖过来,程序还是连的那个数据库,自动将数据库连接字符串写在了App.Config中。
    • 代码中使用DataSet示例:CC_RecordTableAdapter adapter = new CC_RecordTAbleAdapter(); 如何得知Adapter为类名?选中DataSet中下半部分的Adapter,Name属性就是类名。需要右键点击类名→解析
    • 取得所有数据:adapter.GetData(),例子程序:遍历显示所有数据,i<adapter.GetData().Count; adapter.GetData()[i].Age。
    • 常见问题:类名敲不对,表名+TableAdapter,表名+DataTable,表名+Row,然后用“解析”来填充类名。
    • 常见问题:类的内部定义的类要通过包含namespace的全名来引用,不能省略。类的内部定义的类就能避免同一个namespace下类不能重名的问题。
    1. 更新DataSet
    • 调用Adapter的Update方法就可以将DataSet的改变保存到数据库。Adapter.Update(datatable)
    • 要调用Update方法更新必须设置数据库主键,后面的Delete也是如此。
    • 常见错误:“当传递具有已修改行的DataRow集合时,更新要求有效的UpdateCommand”,要为表设置主键。“谁都变了,唯有主键不会变”,程序要通过主键来定位要更新的行,如果没有设定,可以先到数据库中设置主键,然后在DataSet的对应DataTable上点击右键,选择“配置”,在【对话框】中点击完成。
    1. 其它问题
    • 插入新行,调用insert方法。
    • 增加字段怎么办?DataSet设计器中点【配置】,对话框中点【查询生成器】,勾选新增加的字段即可,删除字段同样如此,也可以直接在上面进行修改。
    • 要修改字段就要重新配置生成,这就是强类型DataSet的弱点,因此强类型转换并不一定真的强,还是叫“类型化DataSet”吧。
    • 常见错误:报错:数据为空,判断列的值为空的方法:Is ** Null
    • 为什么Select方法会填充、Update方法会更新,Insert方法会插入?看看Adapter的SelectCommand等属性,是那些SQL语句在起作用,如果在需要完全可以自己手工调整。
    1. 增加新的SQL语句
    • 设计器的Adapter中点右键,选择“添加查询”→“使用SQL语句”,就可以添加多种类型的SQL语句,如果是“Select(返回行)”则SQL语句的列必须是对应DataSet类的父集合,生成两个方法:FillBy*和GetBy*,方法名自己要重新命名,Fillby是将结果填充到现有的DataSet,GetBy是将结果以DataSet方式返回,建议两个都生成,方便以后使用,
    • “Select(返回单个值)”就是ExecuteScalar
    • 对于增加的SQL语句在代码中是以方法的形式使用的。方法的参数类型、顺序就是VS猜测的,如果不正确或者需要调整只要选中对应的语句,然后在【属性】窗口中修改Parameters属性即可。
    • 增加新的SQL语句本质论:探寻源码:不能并发调用。
    • 像使用普通类的方法一样使用Adapter。SQL语句不用再写在界面代码中。这就是一种数据访问层(DAL:Data Access Layer)
    1. 批量插入反应太慢(通过秒表来计时)时,就在执行语句之前,先把连接打开。

                Stopwatch sw = new Stopwatch();

                sw.Start();

                T_UsersTableAdapter adapter = new T_UsersTableAdapter();

                adapter.Connection.Open();

                for (int i = 0; i < 2000; i++)

                {

                    adapter.Insert(i.ToString(),null,i.ToString(),null);

                }

                adapter.Connection.Close();

                sw.Stop();

                MessageBox.Show(sw.Elapsed.ToString());

    1. 示例:向一个表中插入大量随机数据。

                System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

                stopwatch.Start();

     

                //为Test数据库中的T_Product表与T_Manufacturer表生成数据

                string connectionString = "Data Source=.;Initial Catalog=Test;Integrated Security = true";

     

                SqlConnection conn = new SqlConnection(connectionString);

                conn.Open();

                SqlCommand cmd = conn.CreateCommand();

                cmd.Parameters.Add("name", SqlDbType.NVarChar);

                cmd.Parameters.Add("address", SqlDbType.NVarChar);

                cmd.Parameters.Add("email", SqlDbType.NVarChar);

                cmd.Parameters.Add("tel", SqlDbType.VarChar);

                cmd.Parameters.Add("model", SqlDbType.NVarChar);

                cmd.Parameters.Add("builddate", SqlDbType.Date);

     

                double num = 0;

                for (int i = 0; i < 1000; i++)

                {

                    int randomNum = new Random().Next(Int32.MaxValue);

                    DateTime builddate = new DateTime(1998, 1, 1).AddDays(new Random().Next(10000));

     

                    AddDataToManufacturer(cmd, "深圳恒丰塑化材料有限公司" + randomNum, "广东深圳市光明新区" + randomNum, "guangdong" + randomNum + "@163.com", "12345678902" + randomNum, "经销批发" + randomNum, builddate);

                    Console.WriteLine(string.Format("第{0}条完成......", num+1));

                    num++;

     

                    AddDataToManufacturer(cmd, "北京伊特伟业科技公司" + randomNum, "北京市朝阳区" + randomNum, "yite" + randomNum + "@163.com", "12345678903" + randomNum, "个体经营" + randomNum, builddate);

                    Console.WriteLine(string.Format("第{0}条完成......", num + 1));

                    num++;

     

                    AddDataToManufacturer(cmd, "苍南县飞翔彩印厂" + randomNum, "浙江苍南县" + randomNum, "cangnian" + randomNum + "@163.com", "12345678901" + randomNum, "生产厂家" + randomNum, builddate);

                    Console.WriteLine(string.Format("第{0}条完成......", num + 1));

                    num++;

                }

     

                Console.WriteLine(" 完成");

                Console.WriteLine(string.Format("共添加{0}条记录", num));

     

                stopwatch.Stop();

                Console.WriteLine(string.Format("共用{0}时{1}分{2}秒{3}毫秒", stopwatch.Elapsed.Hours,stopwatch.Elapsed.Minutes,stopwatch.Elapsed.Seconds,stopwatch.Elapsed.Milliseconds));

                Console.ReadKey();

            }

     

            /*        

             * 共添加3000条记录

             * 共用0时0分16秒401毫秒

             * 共用0时0分17秒726毫秒

             * 共用0时0分16秒960毫秒

             */

     

            public static void AddDataToManufacturer(SqlCommand cmd,string name, string address,string email,string tel,string model,DateTime builddate)

            {          

                cmd.CommandText = @"insert into T_Manufacturer(ManufacturerName,ManufacturerAddress,ManufacturerEmail,ManufacturerTel,BusinessModel,BuildDate) values(@name,@address,@email,@tel,@model,@builddate)";          

                cmd.Parameters["name"].Value = name;

                cmd.Parameters["address"].Value = address;

                cmd.Parameters["email"].Value = email;

                cmd.Parameters["tel"].Value = tel;

                cmd.Parameters["model"].Value = model;

                cmd.Parameters["builddate"].Value = builddate;

                cmd.ExecuteNonQuery();

            }

     

  • 相关阅读:
    Python爬取网页信息
    C++面向程序设计(第二版)课后习题答案解析
    蓝桥杯——算法分析
    python爬虫——数据爬取和具体解析
    python爬虫——爬取网页数据和解析数据
    Python文件的读写操作
    C++第三章课后作业答案及解析---指针的使用
    C语言蓝桥杯比赛原题和解析
    Web开发技术---简单的登录验证
    C++面向对象程序设计第三章习题答案解析
  • 原文地址:https://www.cnblogs.com/huangf714/p/5876224.html
Copyright © 2011-2022 走看看