net数据访问方法,关于分布式事务的二个误解

背景:

ADO.NET是一组用于和数据源进行交互的面向对象的类库。

      事务是数据库管理系统的一个基本概念,事务具有四个基本特点,即ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),通过事务机制可以保证数据库的一致性和完整性。

核心组件有两个:

      不过数据库事务只能在数据库实例的同一个会话级别进行事务控制。而分布式事务可以协调一个数据库实例多个会话之间的操作,甚至是多个数据库实例之间的数据库操作,并保持事务特性。但是原则上我们不推荐使用分布式事务,因为分布式事务对资源消耗较多,执行效率较差。

  DataSet 是 ADO.NET 的非连接(断开)结构的核心组件。DataSet 的设计目的很明确:为了实现独立于任何数据源的数据访问。因此,ADO.NET结构可以用于多种不同的数据源,用于 XML 数据,或用于管理应用程序本地的数据。DataSet 包含一个或多个 DataTable 对象的集合,这些对象由数据行和数据列以及主键、外键、约束和有关 DataTable 对象中数据的关系信息组成。

      然而一直以来,我们对分布式事务的代码使用和效果都存在误解:使用了TransactionScope就一定会开启分布式事务吗?

      ADO.NET 结构的另一个核心元素是 .NET 数据提供程序(Data Provider)。具体包括:

 

  • Connection 对象提供与数据源的连接。
  • Command对象使您能够访问用于返回数据、修改数据、运行存储过程以及发送或检索参数信息的数据库命令。
  • DataReader 对象从数据源中提供快速的,只读的数据流。
  • DataAdapter 对象提供连接 DataSet 对象和数据源的桥梁。DataAdapter 使用 Command 对象在数据源中执行 SQL 命令,以便将数据加载到 DataSet 中,并使对 DataSet 中数据的更改与数据源保持一致。

验证:

用ADO.NET访问数据一般有两种方法,1.连接模式(直接对数据库进行操作)2.非连接模式(靠数据集)

      我们做一个简单的Demo:两个连接字符串完全相同,ADO.NET会复用连接池中的连接,结果会如何?

1.连接模式(直接对数据库进行操作)

    using (TransactionScope ts = new TransactionScope())
    {
        SqlConnection conn;
        conn = new SqlConnection("server=.;uid=tkk123;pwd=aaaaaa");

        conn.Open();
        SqlCommand cmd = conn.CreateCommand();
        cmd.CommandText = "select 1 as tkk";
        cmd.ExecuteNonQuery();
        conn.Close();

        conn = new SqlConnection("server=.;uid=tkk123;pwd=aaaaaa");
        conn.Open();
        cmd = conn.CreateCommand();
        cmd.CommandText = "select 2 as tkk";
        cmd.ExecuteNonQuery();
        conn.Close();

        ts.Complete();
    }

    Console.WriteLine("OK");
    Console.ReadKey();

步骤:

 

  • 先建立连接字符串 SqlConnection con = new SqlConnection(str)//str是连接数据库的connectionstring
  • 创建command  SqlCommand cmd = new SqlCommand(sql语句,con);
  • 执行读取。cmd.Excutereader()这是获得一个Sqldatareader的结果集,DataReader是一个快速的,轻量级,只读的遍历访问每一行数据的数据流。,用完后记得close因为值允许一个reader,或者 cmd.ExcuteSaclar()查询单个结果 或者cmd.ExecuteNonQuery执行一条语句。

    //构造连接字符串

            SqlConnectionStringBuilder sbd = new SqlConnectionStringBuilder();
            sbd.ConnectionString = ConfigurationManager.ConnectionStrings["delcon"].ConnectionString;//从配置文件得到数据源
            sbd.Pooling = true;
            sbd.MinPoolSize = 0;
            sbd.MaxPoolSize = 50;
            sbd.ConnectTimeout = 10;
            sbd.AsynchronousProcessing = true;//异步连接说明
    

    //拼接SQL字符串

            StringBuilder strSQL = new StringBuilder();
            strSQL.Append("insert into dbo.UserInfo(UserName,UserScore)  ");
            strSQL.Append("OUTPUT inserted.ID values(@UserName,@UserScore)");
    

奇怪的事情发生了,并没有看到我们的以为的分布式事务!!!

command:

图片 1

为了防止被数据库注入,一般都要使用parameter来参数化查询:

 

  //构造Parameter对象
                SqlParameter[] paras = new SqlParameter[] {
                    new SqlParameter("@UserName",SqlDbType.VarChar,20),

                    new SqlParameter("@UserScore", SqlDbType.VarChar,20),

            };
                //给parameter对象赋值
                paras[0].Value = "王杰";
                paras[1].Value = "87";
                //遍历将parameter添加到cmd中
                foreach (var item in paras)
                {
                    cmd.Parameters.Add(item);
                }

我们更改其中的一个连接字符串,使得ADO.NET认为是两个数据源,这样才会真正意义上开启分布式事务。

 

    using (TransactionScope ts = new TransactionScope())
    {
        SqlConnection conn;
        conn = new SqlConnection("server=.;uid=tkk123;pwd=aaaaaa");

        conn.Open();
        SqlCommand cmd = conn.CreateCommand();
        cmd.CommandText = "select 1 as tkk";
        cmd.ExecuteNonQuery();
        conn.Close();

        conn = new SqlConnection("server=.;uid=tkk123;pwd=aaaaaa;"); --加了一个分号,不共享连接
        conn.Open();
        cmd = conn.CreateCommand();
        cmd.CommandText = "select 2 as tkk";
        cmd.ExecuteNonQuery();
        conn.Close();

        ts.Complete();
    }

    Console.WriteLine("OK");
    Console.ReadKey();

必须掌握的几个方法

 ExecuteNonQuery: 执行不返回数据行的操作,并返回一个int类型的数据。

 注意:对于 UPDATE、INSERT 和 DELETE 语句,返回值为该命令所影响的行数。 对于其他所有类型的语句,返回值 为 -1。

 ExecuteReader: 执行查询,并返回一个 DataReader 对象。
 ExecuteScalar: 执行查询,并返回查询结果集中第一行的第一列(object类型)。如果找不到结果集中第一行的第一列,则返回 null 引用。

注: 
1、 User是SQL SERVER的关键字,在表名中应尽量避免,如果必须是User表,可以用SELECT * FROM [User]这样的语句来查询该表的内容 
2、 SqlCommand 类的ExecuteScalar()方法返回的是查询结果的第一行第一列的数据 
3、 在插入数据的同时想要获得当前插入数据的id(主键)号,可以通过output语法和ExecuteScalar方法实现,这是在SQLSERVER 2005以后的版本中都有的,具体语法如下: 
cmd.CommandText = “INSERT INTO T_Users(UserName,PassWord) OUTPUT inserted.Id Values(“admin”, “123456”)”; 
int id = Convert.ToInt32(cmd.ExecuteScalar());


 

2.非连接模式(靠数据集)通过数据集访问数据

  数据集是最常用的访问数据的方式,其主要流程是连接数据库、进行数据查询、将查询结果填充到数据集、对数据集中的数据进行展示和操作、将操作后的数据集提交数据库,通过这一系列操作实现对数据库的修改,此过程主要通过DataSet、DataAdapter、DataTable、DataRow、DataColumn、SqlCommandBuilder等类实现。

  先是查询

  • 1.SqlDataAdapter sqadpt = new SqlDataAdapter(strSQL.ToString(),con);//通过adapter来获得数据
  • //将数据填充到数据集Dataset中
  • SqlCommandBuilder scb = new SqlCommandBuilder(sqadpt );//生成插入、删除、更新语句,这步写上不影响读取

  • 2.DataSet dtset = new DataSet();//先实例化dataset

  • sqadpt.Fill(dtset);//通过adapter来填充dt的实例
  • //现在要通过Datatable来对Dataset中的数据进行展示和修改
  • 3.DataTable dttable = new DataTable();//创建数据表对象
  • dttable = dtset.Tables[0];//dataset来填充datatable数据表对象

  对数据进行修改要用到SqlCommandBuilder的对象实例

具体实例:对数据集的修改,可以通过DataRow以及SqlCommandBuilder对象的实例来实现,

 //创建DataAdapter对象实例 
SqlDataAdapter sqldadp = new SqlDataAdapter(SQL查询语句, con); SqlCommandBuilder scb = new SqlCommandBuilder(sqldadp);//生成插入、删除、更新语句 
DataSet ds = newDataSet();//创建DataSet对象 
sqldadp.Fill(ds);//填充DataSet 
DataTable dttable = new DataTable();//创建数据表对象
dttable = ds.Tables[0];//dataset来填充datatable数据表对象
DataRow Myrow =dttable .NewRow();//在数据表中创建新的空白记录对象
 //从前台页面接受要插入的数据 
Myrow["name"]= Name.Text;
....
 //将接收到的数据添加到DataTable中 
DataRow .Rows.Add(Myrow); //更新数据库中的内容
 sqldadp.Update(ds); //执行Update操作

如果是更新数据的话,也是获得DataTable的某一行后,然后对其中的数据进行赋值修改,然后提交,如果是删除操作,则是通过DataTable获取某行后,用DataRow接受,然后再调用DataTable的Rows属性的Remove方法删除,具体如下代码:

 DataTable dt = ds.Tables[0];
 dt.Rows.Remove(Myrow);

 

网络数据的事务处理

事务处理是为了防止在网络上多用户对数据库的并发操作破坏数据的一致性和完整性,而事务处理主要通过SqlTransaction对象完成,其主要的方法有:Commit()方法主要负责提交事务处理完成真正的数据库查询更新操作,Rollback()方法主要负责当数据查询更新等数据库操作出现异常时的事务回滚。ADO.NET的事务处理主要通过SqlCommand对象来实现事务对象,代码如下:

using (SqlConnection con = new SqlConnection("Data Source = localhost; Initial Catalog = UserDate; Integrated Security = True"))         
{     
SqlTransaction transql = null;//申明SqlTransaction对象的实例    
SqlCommand cmd = new SqlCommand();   
 try    
{          
con.Open();//创建数据库链接          
transql = con.BeginTransaction();//开始事务处理         
cmd.Connection = con;//设置Command对象的链接         
cmd.Transaction = transql;//设置Command对象的事务         
//设置带参数的SQL语句          
cmd.CommandText = "INSERT INTO b1(name, company, position, shijian) VALUES(@Name, @Company, @Position, @Datetime)";         
 //为参数赋值(注意参数在数据库中的数据类型)          
cmd.Parameters.Add(new SqlParameter("Name", Name.Text));          cmd.Parameters.Add(new SqlParameter("Company", Company.Text));         cmd.Parameters.Add(new SqlParameter("Position", Position.Text));         cmd.Parameters.Add(new SqlParameter("Datetime", DateTime.Parse(Datetime.Text)));//注意数据类型转换         
//执行SQL语句         
 cmd.ExecuteNonQuery();        
 //提交事务          
transql.Commit();

Response.Write("添加成功!<br>");     
}
 catch     
{          
//如果程序抛出异常则做事务回滚        
 transql.Rollback();    
 } 
}

注:在上面的代码中,特别要注意SqlTransaction的实例接收的是SqlConnection实例的BeginTransaction()的返回值,SqlCommand的实例的Transaction属性接收的是SqlTransaction的实例,即SqlConnection实例的BeginTransaction()的返回值,而整个事务的过程是由SqlCommand实例来书写的,也就是说,在SqlCommand实例里面的代码相对应的事务都由SqlTransaction的实例自动生成,前提是必须要有transql = con.BeginTransaction();cmd.Transaction = transql;这两句代码,最后用SqlTransaction的Commit()方法或者Rollback()对事务进行提交或者回滚

 

 

 

 

让我们看一下分布式事务是如何协调每个数据库连接,当前的案例我们使用的是同一个数据库,所以虽然建立了两个数据库连接,但最终在数据库层面仍然是同一事务ID。

如果我们打开的是两个不同数据库实例,将会看到什么样的结果呢? try it。。。

图片 2

本文由星彩网app下载发布于计算机编程,转载请注明出处:net数据访问方法,关于分布式事务的二个误解

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。