黑马程序员—ADO.Net基础

黑马程序员—ADO.Net基础

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

 在C#中,使用数据库,不仅可以直接在SQL Server中建立数据库,也可以通过VS自带的mdf内置文件来进行操作。

连接字符串:

程序通过连接字符串指定要连哪台服务器上的、哪个实例的哪个数据库、用什么用户名密码等。

项目内嵌mdf文件形式的连接字符串为

"Data Source = .\SQLEXPRESS;AttachDBFilename = |DataDirectory|\Database1.mdf;Integrated Security = True;User Instance = True"

。其中”.\SQLEXPRESS”表示”本机上的SQLEXPRESS实例”,如果数据库实例名不是SQLEXPRESS,则需要修改。”Database1.mdf”为mdf的文件名。

SqlConnection和SqlCommand

ADO.Net中通过SqlConnection类创建到SQL Server的连接,SqlConeection代表一个数据库连接,如:

SqlConnection conn = new SqlConnection(@"Data Source = .\SQLEXPRESS;AttachDBFilename = |DataDirectory|\Database1.mdf;Integrated Security = True;User Instance = True")

conn.Open();

其中这么写的话还需要conn.Close();conn.Dispose();因为ADO.Net中的连接等资源都实现了IDisposable接口,都需要释放资源。

我们也可以使用using,如:

using (SqlConnection conn = new SqlConnection(@"Data Source = .\SQLEXPRESS;AttachDBFilename = |DataDirectory|\Database1.mdf;Integrated Security = True;User Instance = True"))

            {

                conn.Open();

            }

后面就可以不用conn.Close();conn.Dispose();了,因为出了using的大括号、作用域后,就会自动关闭,这里,出了作用域后,都会判断下有没有Close,如果没有Close就会先Close再调用下Dispose;这里我们顺便说一下Close和Dispose的区别,Close只是关闭链接,以后还能打开,而Dispose是直接销毁链接,不能再次使用。

另外使用SqlConnection的时候,记得要引用程序集using System.Data.SqlClient;

现在我们就可以试着插入数据了,代码如下:

static void Main(string[] args)

        {

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

            }

 

            using (SqlConnection conn = new SqlConnection(@"Data Source = .\SQLEXPRESS;AttachDBFilename = |DataDirectory|\Database1.mdf;Integrated Security = True;User Instance = True"))

            {

                conn.Open();

                using (SqlCommand cmd = conn.CreateCommand())

                {

                    cmd.CommandText = "Insert into Table1(Id,Name) values(1,’王五’')";

                    cmd.ExecuteNonQuery();

                    Console.WriteLine("插入成功");

                }

            }

            Console.ReadKey();

}

其中SqlCommand表示向服务器提交的一个命令(SQL语句等),CommandText属性是要执行的SQL语句。

SqlCommand的ExecuteNonQuery方法:

ExecuteNonQuery方法执行非查询语句(Update、Insert、Delete等);ExecuteNonQuery返回值是执行的影响行数。

SqlCommand的ExecuteScalar方法:

SqlCommand的ExecuteScalar方法用于执行查询,并返回查询所返回的结果集中第一行的第一列,因为不能确定返回值的类型,所以返回值是object类型。

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

using (SqlConnection conn = new SqlConnection(@"Data Source = .\SQLEXPRESS;AttachDBFilename = |DataDirectory|\Database1.mdf;Integrated Security = True;User Instance = True"))

            {

                conn.Open();

                using (SqlCommand cmd = conn.CreateCommand())

                {

                    cmd.CommandText = "Insert into DL(UserName,Password) output inserted.Id values('admin','admin')";

                    int Id = Convert.ToInt32(cmd.ExecuteScalar());

                    Console.WriteLine("插入的信息主键为{0}",Id);

                }

            }

SqlCommand的ExecuteReader方法:

ExecuteReader返回一个有多行的结果集。如:

    using (SqlConnection conn = new SqlConnection(@"Data Source = .\SQLEXPRESS;AttachDBFilename = |DataDirectory|\Database1.mdf;Integrated Security = True;User Instance = True"))

            {

                conn.Open();

                using (SqlCommand cmd = conn.CreateCommand())

                {

                    cmd.CommandText = "select * from DL";

                    using (SqlDataReader reader = cmd.ExecuteReader())

                    {

                        while (reader.Read())

                        {

                            string userName = reader.GetString(reader.GetOrdinal("UserName"));

                            Console.WriteLine(userName);

                        }    

                    }

                }

            }

这里reader.Read()会逐行向前处理,返回bool值,到最后一行,返回false,跳出while循环。GetOrdinal方法可以根据给定列名称获得列序号。

注入漏洞与参数化查询:

如下例:

Console.WriteLine("请输入用户名:");

            string userName = Console.ReadLine();

            Console.WriteLine("请输入密码:");

            string pwd = Console.ReadLine();

 

            using (SqlConnection conn = new SqlConnection(@"Data Source = .\SQLEXPRESS;AttachDBFilename = |DataDirectory|\Database1.mdf;Integrated Security = True;User Instance = True"))

            {

                conn.Open();

                using (SqlCommand cmd = conn.CreateCommand())

                {

                    cmd.CommandText = "select Count(*) from DL where UserName = '" + userName + "'and Password = '" + pwd + "'";

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

                    if (i>0)

                    {

                        Console.WriteLine("登陆成功!");

                    }

                    else

                    {

                        Console.WriteLine("用户名或密码错误!");

                    }

                }

            }

这是一个简单的判断用户名、密码是否正确的小程序。看着没什么错误,其实存在着一个很严重的漏洞,即:当我们的用户名正确的时候,密码我们输入:任意字符串1’ or ’任意字符串2’ = ‘任意字符串2;也是会登陆成功的。这就是一个注入漏洞。

如何才能防止呢?下面,我们提供另外一种写法,以上代码只需要将cmd.CommandText = "select Count(*) from DL where UserName = '" + userName + "'and Password = '" + pwd + "'";修改为

cmd.CommandText = "select Count(*) from DL where UserName = @UserName and Password = @Password";

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

                    cmd.Parameters.Add(new SqlParameter("Password", pwd));

就可以了。

posted @ 2013-04-22 22:30  帅大屋  阅读(160)  评论(0编辑  收藏  举报