您的位置:首页 > 其它

ado.net笔记

2016-09-16 12:25 141 查看
ADO.Net基础

程序要和数据库交互要通过ADO.Net进行,通过ADO.Net就能在程序中执行SQL语句了。ADO.Net中提供了对各种不同数据库的统一操作接口。

直接在项目中内嵌mdf文件的方式使用SQLServer数据库(基于服务的数据库)。mdf文件随着项目走,用起来方便,和在数据库服务器上创建数据库没有什么区别,运行的时候会自动附加(Attach)。

双击mdf文件会在“服务器资源管理器”中打开,管理方式和在Management Studio没有什么本质不同。要拷贝mdf文件需要关闭所有指向mdf文件的连接。

正式生产运行的时候附加到SQLServer上、修改连接字符串即可,除此之外没有任何的区别,在“数据库”节点上点右键“附加”:在数据库点上→任务→分离就可以得到可以拷来的拷去的mdf文件。

用的时候要在控制台、WinForm项目中在Main函数最开始的位置加入备注的代码。ASP.Net项目中不需要。

连接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。

可能出现的问题:连接不上数据库:连接字符串有错误。

执行简单的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’)”;

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

执行查询

执行有多行结果集的用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)

SQL注入漏洞攻击

登录判断:cmd.CommandText = "select count(*) from
Users where UserName = '"+ userName + "' and Password = '" +
password +"'"; 通过将参数拼到SQL语句中。

构造恶意的Password: 12’ or ‘1’ = ‘1 这样的话,就会成功登录了。

防范注入漏洞攻击的方法:不使用SQL语句拼接,通过参数赋值。

查询参数

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直接用添加的值进行比较,因此不会有注入漏洞攻击。

案例

用户界面中进行用户名的增加。输错三次禁止登录,用数据库记录ErrorTimes。

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

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

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

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

数据导出:将用户信息导出到文本文件。

问题:在导入汉字的过程中,如何保存使之在数据库中存入的不是乱码?

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

读取配置中的字符串

将字符串写在代码中缺点:多次重复。

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文件中将连接字符串改变即可。

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”]

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()方法得到变化的结果集,降低传递的资源占用。

可空类型

Int? 可空的int,触决数据库和对int不能为空的不同设置的问题

Int? i3
= 4; int i4 = i3.Value;

弱类型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中填充。

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下类不能重名的问题。

更新DataSet

调用Adapter的Update方法就可以将DataSet的改变保存到数据库。Adapter.Update(datatable)

要调用Update方法更新必须设置数据库主键,后面的Delete也是如此。

常见错误:“当传递具有已修改行的DataRow集合时,更新要求有效的UpdateCommand”,要为表设置主键。“谁都变了,唯有主键不会变”,程序要通过主键来定位要更新的行,如果没有设定,可以先到数据库中设置主键,然后在DataSet的对应DataTable上点击右键,选择“配置”,在【对话框】中点击完成。

其它问题

插入新行,调用insert方法。

增加字段怎么办?DataSet设计器中点【配置】,对话框中点【查询生成器】,勾选新增加的字段即可,删除字段同样如此,也可以直接在上面进行修改。

要修改字段就要重新配置生成,这就是强类型DataSet的弱点,因此强类型转换并不一定真的强,还是叫“类型化DataSet”吧。

常见错误:报错:数据为空,判断列的值为空的方法:Is ** Null

为什么Select方法会填充、Update方法会更新,Insert方法会插入?看看Adapter的SelectCommand等属性,是那些SQL语句在起作用,如果在需要完全可以自己手工调整。

增加新的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)

批量插入反应太慢(通过秒表来计时)时,就在执行语句之前,先把连接打开。

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

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

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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: