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(@"\bin\Debug\") ||

dataDir.EndsWith(@"\bin\Release\"))

{

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("\r\n完成");

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();

}

时间: 2024-10-20 11:05:46

ado.net笔记的相关文章

ADO.NET笔记——使用连接池

相关知识: 连接池的意义: 应用程序往往涉及大量的,并发的数据访问操作 数据库服务器能够同时维系的连接数量非常有限.如果某个数据库访问操作不及时关闭连接,就会减少其他调用对数据库访问的机会.因此,一般需要尽可能晚的打开连接,尽可能早的关闭连接 反复的创建和销毁连接对象,或者反复的打开和关闭实际的连接(从应用程序到数据库服务器,可能跨网络),其开销是比较大的,也是不划算的 采用连接池,在池中缓存若干个链接对象.如果有调用需要使用连接,则从池中取出一个:调用完成后,并不销毁连接,而是将连接放回池中,

ADO.NET笔记——使用DataAdapter执行增删改操作

相关知识: DataSet中的数据一旦从数据库下载下来,便不再与数据库保持联系.如果修改了DataSet中的数据,需要重新建立连接,并且通过SQL命令将修改更新到数据库去 编写SQL命令往往比较繁琐和机械化,ADO.NET提供了一个SqlCommandBuilder对象,帮助DataAdapter对象从SELECT语句推算出需要的UPDATE,DELETE和INSERT语句:然后DataAdapter便可以利用这些语句,检查DataSet中被修改的数据,然后提交到数据库 SqlCommandBu

ADO.NET笔记——调用存储过程

相关知识: 在ADO.NET访问SQL Server时,鼓励使用存储过程取代常规的SQL语句. 存储过程有下列优点: 存储过程中的SQL语句将会经过预先的解析和编译,然后存放在数据库服务器上行.调用的时候不必在此解析语法和编译,因此效率比采用常规SQL语句高 带参数的存储过程在一定程度上可以降低SQL注入攻击的风险 存储过程便于在数据库服务器上统一管理,减少了程序员维护SQL代码的工作量 存储过程有利于重用某些数据库的访问逻辑 代码示例: 在数据库中创建存储过程(沿用SQLInjection案例

ADO.NET笔记——执行事务

相关知识: 处于同一事务(Transaction)内的一组操作,要么都成功执行,最后完全提交:但如果只要有任何一个操作失败或者出问题,所有值钱执行的操作也都取消并恢复到初始状态(即回滚) SqlTransacttion代表从ADO.NET中发出的事务 代码示例: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading

ADO.NET笔记(一)XML导入导出和数据库

数据导出成XML文件 #region 数据导出成XML文件 string sql = "select Id, CallerNumber, TelNum, StartDateTime, EndDateTime from chens.CallRecords"; XDocument xDoc = new XDocument(); //创建根目录 XElement person = new XElement("person"); xDoc.Add(person); usin

菜鸟学习Ado.net笔记一:Ado.net学习之SqlHelper类

1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Data.SqlClient; 5 using System.Data; 6 using Microsoft.Win32; 7 8 namespace SqlHelp 9 { 10 /// <summary> 11 /// 定义SqlParameter所需的参数对象 12 /// </summary> 13

ADO.NET笔记——存储二进制大对象(BLOB)

相关知识 上传二进制大对象(Binary Large Object)(如图片.视频等)的基本编程步骤是: 在数据库中使用varbinary(MAX).varchar(MAX)或者nvarchar(MAX)等数据类型记录BLOB 使用INSERT INTO语句,在表中建立一个新行 使用 UPDATE xxx.WRITE 语句,逐段将BLOB写入大数据字段中 代码示例 数据库:沿用AccountDBforSQLInjection,使用下列的SQL语句建立一个包含varbinary(MAX)字段类型的

ADO.NET笔记——使用通用数据访问

相关知识: 前面所有示例,君是访问特定的数据库(SQL Server),因此注入SqlConnection.SqlCommand.SqlDataReader.SqlDataAdapter等类名都添加了“Sql”的前缀,并且隶属于System.Data.SqlClient命名空间.这就给代码的可移植性带来了巨大问题.如果数据库改用Oracle.MySQL或者BD2,代码几乎要全部重写,非常不利于重用. System.Data.Common命名空间提供了一组类和操作,使得程序可以忽略底层数据库的差异

ADO.NET学习笔记之连接字符串

ADO.NET 2.0学习笔记之连接字符串 刚刚入门不久,想什么学习下dot net平台,就先从数据访问入手吧,从今天开始认真学习ado.net 2.0,为将来发展做好坚实基础. 连接字符串 SQL Client .net数据提供程序在连接到数据库时极其灵活,它提供了多种用以生成连接字符串的方式.可以使用关键字,例如“Data Sourse”.“Initial Catalog”,也可以使用"Server".“Database”等旧术语. 下面是两个例子,用于连接到SqlServer数据