26.2.4. 使用MySQL Connector/NET

26.2.4.1. 前言

在本节中,介绍的Connector/NET的一些常用方式,包括BLOB处理,日期处理,以及与诸如Crystal Reports等常见工具一起使用Connector/NET的方法。

26.2.4.2. 使用MySQL Connector/NET连接到MySQL

26.2.4.2.1. 前言
.NET应用程序和MySQL服务器之间的所有交互均是通过 MySqlConnection对象传送的。在应用程序能够与服务器进行交互之前,必须获取、配置、并打开 MySqlConnection对象。

即使在使用MySqlHelper类时,MySqlConnection对象也会被Helper类创建。

在本节中,介绍了使用MySqlConnection对象连接到MySQL的方法。

26.2.4.2.2. 创建连接字符串

MySqlConnection对象是使用连接字符串配置的。1个连接字符串包含服务器键/值对,由分号隔开。每个键/值对由等号连接。

下面给出了1个简单的连接字符串示例:

    Server=127.0.0.1;Uid=root;Pwd=12345;Database=test;
    

在本例中,对MySqlConnection对象进行了配置,使用用户名root和密码12345与位于127.0.0.1的MySQL服务器相连。所有语句的默认数据库为测试数据库。

典型的选项如下(关于选项的完整清单,请参见API文档):

·         Server:将要连接的MySQL实例的名称或网络地址。默认为本地主机。别名包括Host, Data Source, DataSource, Address, AddrNetwork Address。

·         Uid:连接时使用的MySQL用户账户。别名包括User Id, UsernameUser name。

·         Pwd:MySQL账户的密码。也可以使用别名密码。

·         Database:所有语句作用于的默认数据库。默认为mysql。也可以使用别名Initial Catalog

·         Port:MySQL用于监听连接的端口。默认为3306。将该值指定为“-1将使用命名管道连接。

26.2.4.2.3. 打开连接

一旦创建了连接字符串,可使用它打开与MySQL服务器的连接。

下述代码用于创建MySqlConnection对象,指定连接字符串,并打开连接。

[VB]

Dim conn As New MySql.Data.MySqlClient.MySqlConnection
Dim myConnectionString as String

myConnectionString = "server=127.0.0.1;" _
            & "uid=root;" _
            & "pwd=12345;" _
            & "database=test;"

Try
  conn.ConnectionString = myConnectionString
  conn.Open()

Catch ex As MySql.Data.MySqlClient.MySqlException
  MessageBox.Show(ex.Message)
End Try
  

[C#]

MySql.Data.MySqlClient.MySqlConnection conn;
string myConnectionString;
    
myConnectionString = "server=127.0.0.1;uid=root;" +
    "pwd=12345;database=test;";
  
try
{
    conn = new MySql.Data.MySqlClient.MySqlConnection();
    conn.ConnectionString = myConnectionString;
    conn.Open();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
    MessageBox.Show(ex.Message);
}

你也可以将连接字符串传递给MySqlConnection类的构造函数:

[VB]

Dim myConnectionString as String

myConnectionString = "server=127.0.0.1;" _
              & "uid=root;" _
              & "pwd=12345;" _
              & "database=test;" 

Try
    Dim conn As New MySql.Data.MySqlClient.MySqlConnection(myConnectionString)
    conn.Open()
Catch ex As MySql.Data.MySqlClient.MySqlException
   MessageBox.Show(ex.Message)
End Try
  

[C#]

MySql.Data.MySqlClient.MySqlConnection conn;
string myConnectionString;

myConnectionString = "server=127.0.0.1;uid=root;" +
    "pwd=12345;database=test;";

try
{
    conn = new MySql.Data.MySqlClient.MySqlConnection(myConnectionString);
    conn.Open();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
    MessageBox.Show(ex.Message);
}
一旦打开了连接,其他MySQL Connector/NET类也能使用该连接与MySQL服务器进行通信。
26.2.4.2.4. 处理连接错误

由于与外部服务器的连接不可预测,应为你的.NET应用程序添加错误处理功能,这点很重要。出现连接错误时,MySqlConnection类将返回1个MySqlException对象。该对象有两个在处理错误时十分有用的属性:

·         Message:描述当前异常的消息。

·         Number:MySQL错误编号。

处理错误时,可根据错误编号了解应用程序的响应。进行连接时最常见的两个错误编号如下:

·         0: 无法连接到服务器。

·         1045: 无效的用户名和/或密码。

在下面的代码中,介绍了根据实际错误改编应用程序的方法:

[VB]

Dim myConnectionString as String

myConnectionString = "server=127.0.0.1;" _
          & "uid=root;" _
          & "pwd=12345;" _
          & "database=test;" 

Try
    Dim conn As New MySql.Data.MySqlClient.MySqlConnection(myConnectionString)
    conn.Open()
Catch ex As MySql.Data.MySqlClient.MySqlException
    Select Case ex.Number
        Case 0
            MessageBox.Show("Cannot connect to server. Contact administrator")
        Case 1045
            MessageBox.Show("Invalid username/password, please try again")
    End Select
End Try
  

[C#]

MySql.Data.MySqlClient.MySqlConnection conn;
string myConnectionString;

myConnectionString = "server=127.0.0.1;uid=root;" +  
    "pwd=12345;database=test;";

try
{
    conn = new MySql.Data.MySqlClient.MySqlConnection(myConnectionString);
    conn.Open();
}
    catch (MySql.Data.MySqlClient.MySqlException ex)
{
    switch (ex.Number)
    {
        case 0:
            MessageBox.Show("Cannot connect to server.  Contact administrator");
        case 1045:
            MessageBox.Show("Invalid username/password, please try again");
    }
}
  

26.2.4.3. 与预处理语句一起使用MySQL Connector/NET

26.2.4.3.1. 前言
从MySQL 4.1开始,能够与MySQL Connector/NET一起使用预处理语句。使用预处理语句能够现住改善多次执行的查询的性能。

对于多次执行的语句,预处理执行的速度快于直接执行,这是因为只需进行1次解析操作。在直接执行的情况下,每次执行时均将进行解析操作。预处理执行还能降低网络通信量,这是因为对于预处理语句的每次执行,仅需发送用于参数的数据。

预处理语句的另一优点是,它能使用二进制协议,这使得客户端和服务器间的数据传输更有效率。

26.2.4.3.2. 在MySQL Connector/NET中准备语句
为了准备好语句,需创建1个命令对象,并为查询设置 .CommandText属性。

输入语句后,调用MySqlCommand对象的.Prepare方法。完成语句的准备后,为查询中的每个元素添加参数。

输入查询并输入参数后,使用.ExecuteNonQuery().ExecuteScalar()、或.ExecuteReader方法执行语句。

对于后续的执行操作,仅需更改参数值并再次调用执行方法,无需设置.CommandText属性或重新定义参数。

[VB]

Dim conn As New MySqlConnection
Dim cmd As New MySqlCommand
  
conn.ConnectionString = strConnection
 
Try
   conn.Open()
   cmd.Connection = conn
 
   cmd.CommandText = "INSERT INTO myTable VALUES(NULL, ?number, ?text)"
   cmd.Prepare()
 
   cmd.Parameters.Add("?number", 1)
   cmd.Parameters.Add("?text", "One")
 
   For i = 1 To 1000
       cmd.Parameters("?number").Value = i
       cmd.Parameters("?text").Value = "A string value"
 
       cmd.ExecuteNonQuery()
     Next 
Catch ex As MySqlException
    MessageBox.Show("Error " & ex.Number & " has occurred: " & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
  

[C#]

MySql.Data.MySqlClient.MySqlConnection conn;
MySql.Data.MySqlClient.MySqlCommand cmd;
  
conn = new MySql.Data.MySqlClient.MySqlConnection();
cmd = new MySql.Data.MySqlClient.MySqlCommand();
 
conn.ConnectionString = strConnection;
 
try
{
    conn.Open();
    cmd.Connection = conn;
 
    cmd.CommandText = "INSERT INTO myTable VALUES(NULL, ?number, ?text)";
    cmd.Prepare();
 
    cmd.Parameters.Add("?number", 1);
    cmd.Parameters.Add("?text", "One");
 
    for (int i=1; i <= 1000; i++)
    {
        cmd.Parameters["?number"].Value = i;
        cmd.Parameters["?text"].Value = "A string value";
 
        cmd.ExecuteNonQuery();
    }
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
    MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
        "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

26.2.4.4. 用MySQL Connector/NET访问存储程序

26.2.4.4.1. 前言

随着MySQL版本5的发布,MySQL服务器目前支持存储程序,它采用了SQL 2003存储程序的语法。

 

存储程序指的是能够保存在服务器上的一组SQL语句。 一旦完成了该操作,客户端无需再次发出单独语句,而仅需引用存储程序取而代之。

在下述情况下,存储程序尤其有用:

·         多个客户端应用程序是采用不同语言编写的或工作在不同平台上,但需执行相同的数据库操作。

·         安全性极其重要时。例如,对于所有共同操作,银行采用了存储程序。这样,就能提供一致且安全的环境,而且这类存储程序能够保证每次操作均具有恰当登录。在这类设置下,应用程序和用户无法直接访问数据库表,但能执行特定的存储程序。

MySQL Connector/NET支持通过MySqlCommand对象的存储程序调用。使用MySqlCommand.Parameters集,能够将数据传入和传出MySQL存储程序。

在本节中,未深度介绍创建存储程序方面的信息,要想了解这类信息,请参见MySQL参考手册的存储程序

在MySQL Connector/NET安装的Samples目录下,可找到1个相应的示例,该示例演示了与MySQL Connector/NET一起使用存储程序的方法。

26.2.4.4.2. 从MySQL Connector/NET创建存储程序

可使用多种工具创建MySQL中的存储程序。首先,可使用mysql命令行客户端创建存储程序。其次,可使用MySQL Query Browser GUI客户端创建存储程序。最后,可使用MySqlCommand对象的.ExecuteNonQuery方法创建存储程序。

[VB]

Dim conn As New MySqlConnection
Dim cmd As New MySqlCommand

conn.ConnectionString = "server=127.0.0.1;" _
    & "uid=root;" _
    & "pwd=12345;" _
    & "database=test"

Try
    conn.Open()
    cmd.Connection = conn

    cmd.CommandText = "CREATE PROCEDURE add_emp(" _
        & "IN fname VARCHAR(20), IN lname VARCHAR(20), IN bday DATETIME, OUT empno INT) " _
        & "BEGIN INSERT INTO emp(first_name, last_name, birthdate) " _
        & "VALUES(fname, lname, DATE(bday)); SET empno = LAST_INSERT_ID(); END"
 
    cmd.ExecuteNonQuery()
Catch ex As MySqlException
    MessageBox.Show("Error " & ex.Number & " has occurred: " & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try

[C#]

MySql.Data.MySqlClient.MySqlConnection conn;
MySql.Data.MySqlClient.MySqlCommand cmd;

conn = new MySql.Data.MySqlClient.MySqlConnection();
cmd = new MySql.Data.MySqlClient.MySqlCommand();

conn.ConnectionString = "server=127.0.0.1;uid=root;" +
    "pwd=12345;database=test;";

try
{
    conn.Open();
    cmd.Connection = conn;

    cmd.CommandText = "CREATE PROCEDURE add_emp(" +
        "IN fname VARCHAR(20), IN lname VARCHAR(20), IN bday DATETIME, OUT empno INT) " +
        "BEGIN INSERT INTO emp(first_name, last_name, birthdate) " +
        "VALUES(fname, lname, DATE(bday)); SET empno = LAST_INSERT_ID(); END";

    cmd.ExecuteNonQuery();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
请注意,不同于命令行和GUI客户端,在MySQL Connector/NET中创建存储程序时不需要指定特殊的定界符。
26.2.4.4.3. 从MySQL Connector/NET调用存储程序

要想使用MySQL Connector/NET来调用存储程序,应创建1个MySqlCommand对象,并将存储程序名作为.CommandText属性传递。.CommandType属性设置为CommandType.StoredProcedure。

命名了存储程序后,为存储程序中的每个参数创建1个MySqlCommand参数。用参数名和包含值的对象定义IN参数,用参数名和预计将返回的数据类型定义OUT参数。对于所有参数,均需定义参数方向。

定义完参数后,使用MySqlCommand.ExecuteNonQuery()方法调用存储程序。

[VB]

Dim conn As New MySqlConnection
Dim cmd As New MySqlCommand

conn.ConnectionString = "server=127.0.0.1;" _
    & "uid=root;" _
    & "pwd=12345;" _
    & "database=test"

Try
    conn.Open()
    cmd.Connection = conn

    cmd.CommandText = "add_emp"
    cmd.CommandType = CommandType.StoredProcedure

    cmd.Parameters.Add("?lname", 'Jones')
    cmd.Parameters("?lname").Direction = ParameterDirection.Input

    cmd.Parameters.Add("?fname", 'Tom')
    cmd.Parameters("?fname").Direction = ParameterDirection.Input

    cmd.Parameters.Add("?bday", #12/13/1977 2:17:36 PM#)
    cmd.Parameters("?bday").Direction = ParameterDirection.Input

    cmd.Parameters.Add("?empno", MySqlDbType.Int32)
    cmd.Parameters("?empno").Direction = ParameterDirection.Output

    cmd.ExecuteNonQuery()

    MessageBox.Show(cmd.Parameters("?empno").Value)
Catch ex As MySqlException
    MessageBox.Show("Error " & ex.Number & " has occurred: " & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try

[C#]

MySql.Data.MySqlClient.MySqlConnection conn;
MySql.Data.MySqlClient.MySqlCommand cmd;

conn = new MySql.Data.MySqlClient.MySqlConnection();
cmd = new MySql.Data.MySqlClient.MySqlCommand();

conn.ConnectionString = "server=127.0.0.1;uid=root;" +
    "pwd=12345;database=test;";

try
{
    conn.Open();
    cmd.Connection = conn;

    cmd.CommandText = "add_emp";
    cmd.CommandType = CommandType.StoredProcedure;

    cmd.Parameters.Add("?lname", "Jones");
    cmd.Parameters("?lname").Direction = ParameterDirection.Input;

    cmd.Parameters.Add("?fname", "Tom");
    cmd.Parameters("?fname").Direction = ParameterDirection.Input;

    cmd.Parameters.Add("?bday", DateTime.Parse("12/13/1977 2:17:36 PM"));
    cmd.Parameters("?bday").Direction = ParameterDirection.Input;

    cmd.Parameters.Add("?empno", MySqlDbType.Int32);
    cmd.Parameters("?empno").Direction = ParameterDirection.Output;

    cmd.ExecuteNonQuery();

    MessageBox.Show(cmd.Parameters("?empno").Value);
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
    MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
      "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
一旦调用了存储程序,可使用 MySqlConnector.Parameters集的.Value属性检索输出参数的值。

26.2.4.5. 用Connector/NET处理BLOB数据

26.2.4.5.1. 前言
MySQL的1种用途是在BLOB列中保存二进制数据。MySQL支持4种不同的BLOB数据类型: TINYBLOB, BLOB, MEDIUMBLOBLONGBLOB

可使用Connector/NET访问保存在BLOB列中的数据,并能使用客户端代码对这类数据进行操作。使用Connector/NET和BLOB数据时,无特殊要求。

在本节中,给出了数个简单的代码示例,在MySQL Connector/NET安装的Samples目录下,可找到1个完整的示例应用程序。

26.2.4.5.2. 准备MySQL服务器
与BLOB数据一起使用MySQL的第1步是配置服务器。首先,让我们从创建要访问的表开始。在我的文件表中,通常有4列:1个具有恰当大小的AUTO_INCREMENT列(UNSIGNED SMALLINT),用于保存识别文件的主键;1个VARCHAR列,用于保存文件名;1个UNSIGNED MEDIUMINT列,用于保存文件的大小;以及1个用于保存文件本身的MEDIUMBLOB列。对于本例,我将使用下述表定义:
CREATE TABLE file(
file_id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
file_name VARCHAR(64) NOT NULL,
file_size MEDIUMINT UNSIGNED NOT NULL,
file MEDIUMBLOB NOT NULL);

完成表的创建后,或许需要更改max_allowed_packet系统变量。该变量决定了能够发送给MySQL服务器的信息包(即单个行)大小。默认情况下,服务器能够接受来自客户端应用程序的信息包最大为1MB。如果不打算超过1MB,情况良好。如果打算在文件传输中超出1MB,必须增加该数值。

可以使用“MySQL系统管理员的启动变量”屏幕更改max_allowed_packet选项。在“联网”选项卡的“内存”部分,恰当调整“允许的最大值”选项。完成值的调整后,点击“应用更改”按钮,并使用“MySQL管理员”的“服务控制”屏幕重新启动服务器。也可以在my.cnf文件中直接调整该值(添加1行,max_allowed_packet=xxM),或在MySQL中使用SET max_allowed_packet=xxM。

设置max_allowed_packet时应保守些,这是因为传输BLOB数据需要一段时间。恰当地设置该值,使之与预期使用相符,并在必要时增大该值。

26.2.4.5.3. 将文件写入数据库

要想将文件写入数据库,需要将文件转换为字节数组,然后将字节数组用作INSERT查询的参数。

在下述代码中,使用FileStream对象打开了1个文件,将其读入至字节数组,然后将其插入到文件表中:

[VB]

Dim conn As New MySqlConnection
Dim cmd As New MySqlCommand

Dim SQL As String

Dim FileSize As UInt32
Dim rawData() As Byte
Dim fs As FileStream

conn.ConnectionString = "server=127.0.0.1;" _
    & "uid=root;" _
    & "pwd=12345;" _
    & "database=test"

Try
    fs = New FileStream("c:\image.png", FileMode.Open, FileAccess.Read)
    FileSize = fs.Length
    
    rawData = New Byte(FileSize) {}
    fs.Read(rawData, 0, FileSize)
    fs.Close()
    
    conn.Open()
    
    SQL = "INSERT INTO file VALUES(NULL, ?FileName, ?FileSize, ?File)"
    
    cmd.Connection = conn
    cmd.CommandText = SQL
    cmd.Parameters.Add("?FileName", strFileName)
    cmd.Parameters.Add("?FileSize", FileSize)
    cmd.Parameters.Add("?File", rawData)
    
    cmd.ExecuteNonQuery()
    
    MessageBox.Show("File Inserted into database successfully!", _
    "Success!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
    
    conn.Close()
Catch ex As Exception
    MessageBox.Show("There was an error: " & ex.Message, "Error", _
        MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
  

[C#]

MySql.Data.MySqlClient.MySqlConnection conn;
MySql.Data.MySqlClient.MySqlCommand cmd;

conn = new MySql.Data.MySqlClient.MySqlConnection();
cmd = new MySql.Data.MySqlClient.MySqlCommand();

string SQL;
UInt32 FileSize;
byte[] rawData;
FileStream fs;

conn.ConnectionString = "server=127.0.0.1;uid=root;" +
    "pwd=12345;database=test;";

try
{
    fs = new FileStream(@"c:\image.png", FileMode.Open, FileAccess.Read);
    FileSize = fs.Length;

    rawData = new byte[FileSize];
    fs.Read(rawData, 0, FileSize);
    fs.Close();

    conn.Open();

    SQL = "INSERT INTO file VALUES(NULL, ?FileName, ?FileSize, ?File)";

    cmd.Connection = conn;
    cmd.CommandText = SQL;
    cmd.Parameters.Add("?FileName", strFileName);
    cmd.Parameters.Add("?FileSize", FileSize);
    cmd.Parameters.Add("?File", rawData);

    cmd.ExecuteNonQuery();

    MessageBox.Show("File Inserted into database successfully!",
        "Success!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);

    conn.Close();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
    MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
        "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
 
FileStream对象的Read方法可用于将文件加载到字节数组中,该字节数组的大小是根据FileStream对象的Length属性确定的。

将字节数组指定为MySqlCommand对象的参数后,调用ExecuteNonQuery方法,并将BLOB插入到文件表中。

26.2.4.5.4. 将BLOB从数据库读取到磁盘上的文件

一旦将文件加载到了文件表中,就能使用MySqlDataReader类来检索它。

在下述代码中,从文件表提取了1行,然后将数据装载到要写入至磁盘的FileStream对象。

[VB]

Dim conn As New MySqlConnection
Dim cmd As New MySqlCommand
Dim myData As MySqlDataReader
Dim SQL As String
Dim rawData() As Byte
Dim FileSize As UInt32
Dim fs As FileStream

conn.ConnectionString = "server=127.0.0.1;" _
    & "uid=root;" _
    & "pwd=12345;" _
    & "database=test"

SQL = "SELECT file_name, file_size, file FROM file"

Try
    conn.Open()
    
    cmd.Connection = conn
    cmd.CommandText = SQL
    
    myData = cmd.ExecuteReader
    
    If Not myData.HasRows Then Throw New Exception("There are no BLOBs to save")
    
    myData.Read()
    
    FileSize = myData.GetUInt32(myData.GetOrdinal("file_size"))
    rawData = New Byte(FileSize) {}
    
    myData.GetBytes(myData.GetOrdinal("file"), 0, rawData, 0, FileSize)
    
    fs = New FileStream("C:\newfile.png", FileMode.OpenOrCreate, FileAccess.Write)
    fs.Write(rawData, 0, FileSize)
    fs.Close()
    
    MessageBox.Show("File successfully written to disk!", "Success!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
    
    myData.Close()
    conn.Close()
Catch ex As Exception
    MessageBox.Show("There was an error: " & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
  

[C#]

MySql.Data.MySqlClient.MySqlConnection conn;
MySql.Data.MySqlClient.MySqlCommand cmd;
MySql.Data.MySqlClient.MySqlDataReader myData;

conn = new MySql.Data.MySqlClient.MySqlConnection();
cmd = new MySql.Data.MySqlClient.MySqlCommand();

string SQL;
UInt32 FileSize;
byte[] rawData;
FileStream fs;

conn.ConnectionString = "server=127.0.0.1;uid=root;" +
    "pwd=12345;database=test;";

SQL = "SELECT file_name, file_size, file FROM file";

try
{
    conn.Open();

    cmd.Connection = conn;
    cmd.CommandText = SQL;

    myData = cmd.ExecuteReader();

    if (! myData.HasRows)
        throw new Exception("There are no BLOBs to save");

    myData.Read();

    FileSize = myData.GetUInt32(myData.GetOrdinal("file_size"));
    rawData = new byte[FileSize];

    myData.GetBytes(myData.GetOrdinal("file"), 0, rawData, 0, FileSize);

    fs = new FileStream(@"C:\newfile.png", FileMode.OpenOrCreate, FileAccess.Write);
    fs.Write(rawData, 0, FileSize);
    fs.Close();

    MessageBox.Show("File successfully written to disk!",
        "Success!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);

    myData.Close();
    conn.Close();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
    MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
        "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
 
连接后,文件表的内容将被加载到 MySqlDataReader对象中。使用MySqlDataReader的 GetBytes方法将BLOB加载到字节数组,然后使用FileStream对象将字节数据写入磁盘。

MySqlDataReader的GetOrdinal方法可用于确定命名列的整数索引。如果SELECT查询的列顺序发生变化,使用GetOrdinal方法能够防止错误。

26.2.4.6. 与Crystal Reports一起使用MySQL Connector/NET

26.2.4.6.1. 前言
Crystal Reports是Windows应用程序开发人员用于通报文档生成的常用工具。在本节中,介绍了Crystal Reports XI与MySQL和Connector/NET一起使用的方法。

在MySQL Connector/NET安装的Samples目录的CrystalDemo子目录下,可找到完整的示例应用程序。

26.2.4.6.2. 创建数据源

在Crystal Reports中创建报告时,在设计报告时,有两个用于访问MySQL数据的选项。

第1个选项是,设计报告时,使用Connector/ODBC作为ADO数据源。你能够浏览数据库,并使用拖放式操作选择表和字段以创建报告。该方法的缺点是,必须在应用程序中执行额外操作以生成与报告预期的数据集匹配的数据集。

第2个选项是在VB.NET中创建数据集,并将其保存为XML格式。随后,该XML文件可被用于设计报告。在应用程序中显示报告时,它的表现相当良好,但设计时的通用性较差,这是因为在创建数据集时,必须选择所有的相关列。如果忘记选择了某一列,在能够将列添加到报告前,必须重新创建数据集。

使用下述代码,可根据查询操作创建数据集,并将其写入磁盘。

[VB]

Dim myData As New DataSet
Dim conn As New MySqlConnection
Dim cmd As New MySqlCommand
Dim myAdapter As New MySqlDataAdapter

conn.ConnectionString = "server=127.0.0.1;" _
    & "uid=root;" _
    & "pwd=12345;" _
    & "database=world"

Try
    conn.Open()
    cmd.CommandText = "SELECT city.name AS cityName, city.population AS CityPopulation, " _ 
        & "country.name, country.population, country.continent " _
        & "FROM country, city ORDER BY country.continent, country.name"
    cmd.Connection = conn
    
    myAdapter.SelectCommand = cmd
    myAdapter.Fill(myData)
    
    myData.WriteXml("C:\dataset.xml", XmlWriteMode.WriteSchema)
Catch ex As Exception
    MessageBox.Show(ex.Message, "Report could not be created", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
 

[C#]

DataSet myData = new DataSet();
MySql.Data.MySqlClient.MySqlConnection conn;
MySql.Data.MySqlClient.MySqlCommand cmd;
MySql.Data.MySqlClient.MySqlDataAdapter myAdapter;

conn = new MySql.Data.MySqlClient.MySqlConnection();
cmd = new MySql.Data.MySqlClient.MySqlCommand();
myAdapter = new MySql.Data.MySqlClient.MySqlDataAdapter();

conn.ConnectionString = "server=127.0.0.1;uid=root;" +
  "pwd=12345;database=test;";
  
try
{
  cmd.CommandText = "SELECT city.name AS cityName, city.population AS CityPopulation, " +
  "country.name, country.population, country.continent " +
  "FROM country, city ORDER BY country.continent, country.name";
  cmd.Connection = conn;
  
  myAdapter.SelectCommand = cmd;
  myAdapter.Fill(myData);
  
  myData.WriteXml(@"C:\dataset.xml", XmlWriteMode.WriteSchema);
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
  MessageBox.Show(ex.Message, "Report could not be created",
  MessageBoxButtons.OK, MessageBoxIcon.Error);
}
设计报告时,可将该代码生成的XML文件用作ADO.NET XML数据源。

如果你选择使用Connector/ODBC来设计报告,可从dev.mysql.com下载它。

26.2.4.6.3. 创建报告
对于大多数应用目的,标准的报告向导应能帮助你完成报告的最初创建。要想启动向导,打开Crystal Reports并从“文件”菜单选择“New > Standard Report”选项。

向导首先要求你提供数据源。如果你正使用Connector/ODBC作为数据源,选择数据源时,请使用OLE DB (ADO)树的“用于ODBC的OLEDB provider”选项,,而不是来自ODBC (RDO)的对应选项。如果你使用的是已保存的数据集,请选择ADO.NET (XML)选项,并浏览你保存的数据集。

在报告的创建过程中,剩余部分将由向导自动完成。

创建完报告后,选择“文件”菜单中的“Report Options...”菜单项。取消对“Save Data With Report”(与报告一起保存数据)选项的选择。这样,就能防止保存的数据干扰应用程序中的数据加载操作。

26.2.4.6.4. 显示报告

要想显示报告,首先用报告所需的数据填充数据集,然后加载报告,并将其与绑定到数据集。最后,将报告传递给crViewer控制,以便向用户显示它。

在显示报告的项目中,需要下述引用:

·         CrytalDecisions.CrystalReports.Engine

·         CrystalDecisions.ReportSource

·         CrystalDecisions.Shared

·         CrystalDecisions.Windows.Forms

在下述代码中,假定你使用数据集(用创建数据源中给出的代码保存的数据集)创建了报告,并在名为“myViewer”的表单上有1个crViewer控件。

[VB]

Imports CrystalDecisions.CrystalReports.Engine
Imports System.Data
Imports MySql.Data.MySqlClient

Dim myReport As New ReportDocument
Dim myData As New DataSet
Dim conn As New MySqlConnection
Dim cmd As New MySqlCommand
Dim myAdapter As New MySqlDataAdapter

conn.ConnectionString = _
    "server=127.0.0.1;" _
    & "uid=root;" _
    & "pwd=12345;" _
    & "database=test"

Try
    conn.Open()
    
    cmd.CommandText = "SELECT city.name AS cityName, city.population AS CityPopulation, " _ 
        & "country.name, country.population, country.continent " _
        & "FROM country, city ORDER BY country.continent, country.name"
    cmd.Connection = conn
    
    myAdapter.SelectCommand = cmd
    myAdapter.Fill(myData)
    
    myReport.Load(".\world_report.rpt")
    myReport.SetDataSource(myData)
    myViewer.ReportSource = myReport
Catch ex As Exception
    MessageBox.Show(ex.Message, "Report could not be created", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try

[C#]

using CrystalDecisions.CrystalReports.Engine;
using System.Data;
using MySql.Data.MySqlClient;

ReportDocument myReport = new ReportDocument();
DataSet myData = new DataSet();
MySql.Data.MySqlClient.MySqlConnection conn;
MySql.Data.MySqlClient.MySqlCommand cmd;
MySql.Data.MySqlClient.MySqlDataAdapter myAdapter;

conn = new MySql.Data.MySqlClient.MySqlConnection();
cmd = new MySql.Data.MySqlClient.MySqlCommand();
myAdapter = new MySql.Data.MySqlClient.MySqlDataAdapter();

conn.ConnectionString = "server=127.0.0.1;uid=root;" +
    "pwd=12345;database=test;";

try
{
    cmd.CommandText = "SELECT city.name AS cityName, city.population AS CityPopulation, " +
        "country.name, country.population, country.continent " +
        "FROM country, city ORDER BY country.continent, country.name";
    cmd.Connection = conn;

    myAdapter.SelectCommand = cmd;
    myAdapter.Fill(myData);

    myReport.Load(@".\world_report.rpt");
    myReport.SetDataSource(myData);
    myViewer.ReportSource = myReport;
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
    MessageBox.Show(ex.Message, "Report could not be created",
        MessageBoxButtons.OK, MessageBoxIcon.Error);
}

使用相同的查询(用于生成前面保存的数据集),可生成新的数据集。一旦填充了数据集,可使用ReportDocument加载报告文件,并将其与数据集绑定在一起。ReportDocument是作为crViewer的ReportSource而传递的。

使用Connector/ODBC从单个表创建报告时,采用了相同的方法。数据集替换报告中使用的表,并恰当显示报告。

如果报告是使用Connector/ODBC从多个表创建的,在我们的应用程序中必须创建具有多个表的数据集。这样,就能用数据集中的报告替换报告数据源中的各个表。

在我们的MySqlCommand对象中提供多条SELECT语句,通过该方式,用多个表填充数据集。这些SELECT语句基于SQL查询,如数据库菜单“Show SQL Query”选项中的“Crystal Reports”中显示的那样。假定有下述查询:

SELECT `country`.`Name`, `country`.`Continent`, `country`.`Population`, `city`.`Name`, `city`.`Population`
FROM `world`.`country` `country` LEFT OUTER JOIN `world`.`city` `city` ON `country`.`Code`=`city`.`CountryCode`
ORDER BY `country`.`Continent`, `country`.`Name`, `city`.`Name`

该查询将被转换为两条SELECT查询,并以下述代码显示:

[VB]

Imports CrystalDecisions.CrystalReports.Engine
Imports System.Data
Imports MySql.Data.MySqlClient

Dim myReport As New ReportDocument
Dim myData As New DataSet
Dim conn As New MySqlConnection
Dim cmd As New MySqlCommand
Dim myAdapter As New MySqlDataAdapter

conn.ConnectionString = "server=127.0.0.1;" _
    & "uid=root;" _
    & "pwd=12345;" _
    & "database=world"

Try
    conn.Open()
    cmd.CommandText = "SELECT name, population, countrycode FROM city ORDER BY countrycode, name; " _
        & "SELECT name, population, code, continent FROM country ORDER BY continent, name"
    cmd.Connection = conn
    
    myAdapter.SelectCommand = cmd
    myAdapter.Fill(myData)
    
    myReport.Load(".\world_report.rpt")
    myReport.Database.Tables(0).SetDataSource(myData.Tables(0))
    myReport.Database.Tables(1).SetDataSource(myData.Tables(1))
    myViewer.ReportSource = myReport
Catch ex As Exception
    MessageBox.Show(ex.Message, "Report could not be created", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try

[C#]

using CrystalDecisions.CrystalReports.Engine;
using System.Data;
using MySql.Data.MySqlClient;

ReportDocument myReport = new ReportDocument();
DataSet myData = new DataSet();
MySql.Data.MySqlClient.MySqlConnection conn;
MySql.Data.MySqlClient.MySqlCommand cmd;
MySql.Data.MySqlClient.MySqlDataAdapter myAdapter;

conn = new MySql.Data.MySqlClient.MySqlConnection();
cmd = new MySql.Data.MySqlClient.MySqlCommand();
myAdapter = new MySql.Data.MySqlClient.MySqlDataAdapter();

conn.ConnectionString = "server=127.0.0.1;uid=root;" +
    "pwd=12345;database=test;";

try
{
    cmd.CommandText = "SELECT name, population, countrycode FROM city ORDER " +
        "BY countrycode, name; SELECT name, population, code, continent FROM " +
        "country ORDER BY continent, name";
    cmd.Connection = conn;

    myAdapter.SelectCommand = cmd;
    myAdapter.Fill(myData);

    myReport.Load(@".\world_report.rpt");
    myReport.Database.Tables(0).SetDataSource(myData.Tables(0));
    myReport.Database.Tables(1).SetDataSource(myData.Tables(1));
    myViewer.ReportSource = myReport;
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
    MessageBox.Show(ex.Message, "Report could not be created",
        MessageBoxButtons.OK, MessageBoxIcon.Error);
}  
 
应将SELECT语句按字母顺序排列,这点很重要,原因在于,这是报告希望其源表所具有的顺序。对于报告中的每个表,均需要一条SetDataSource语句。

该方法会导致性能问题,这是因为Crystal Reports必须在客户端一侧将表绑定在一起,与使用以前保存的数据集相比,速度较慢。

26.2.4.7. 在MySQL Connector/NET中处理日期和时间信息

26.2.4.7.1. 前言
MySQL和.NET语言处理日期和时间信息的方式是不同的,MySQL允许使用无法由.NET数据类型表示的日期,如“ 0000-00-00 00:00:00如果处理步当,该差异会导致问题。

在本节中,介绍了使用MySQL Connector/NET时恰当处理日期和时间信息的方法。

26.2.4.7.2. 使用无效日期时的问题
对于使用无效日期的开发人员来说,数据处理方面的差异会导致问题。无效的MySQL日期无法被加载到.NET DateTime对象中,包括 NULL日期。

由于该原因,不能用MySqlDataAdapter类的Fill方法填充.NET DataSet对象,这是因为无效日期会导致System.ArgumentOutOfRangeException异常。

26.2.4.7.3. 限制无效日期
对日期问题的最佳解决方案是,限制用户输入无效日期。这即可在客户端上进行,也可在服务器端进行。

在客户端上限制无效日期十分简单,即总使用.NET DateTime类来处理日期。DateTime类仅允许有效日期,从而确保了数据库中的值也是有效的。该方法的缺点是,在使用.NET和非.NET代码操作数据库的混合环境下不能使用它,这是因为各应用程序必须执行自己的日期验证。

MySQL 5.0.2和更高版本的用户可使用新的传统SQL模式来限制无效日期值。关于使用传统SQL模式的更多信息,请参见http://dev.mysql.com/doc/mysql/en/server-sql-mode.html

26.2.4.7.4. 处理无效日期
强烈建议在你的.NET应用程序中应避免使用无效日期,尽管如此,也能tongguo MySqlDateTime数据类型使用无效日期。

MySqlDateTime数据类型支持MySQL服务器支持的相同日期值。MySQL Connector/NET的默认行为是,对有效的日期值返回1个.NET DateTime对象,对无效日期值返回错误。可以更改该默认方式,使MySQL Connector/NET为无效日期返回MySqlDateTime对象。

要想使MySQL Connector/NET为无效日期返回MySqlDateTime对象,可在连接字符串中添加下行:

  Allow Zero Datetime=True
  

请注意,使用MySqlDateTime类仍会产生问题。下面介绍了一些已知问题:

1.    无效日期的数据绑定仍会导致错误(零日期0000-00-00看上去不存在该问题)。

2.    ToString方法返回按标准MySQL格式进行格式处理的日期(例如,2005-02-23 08:50:25)。这与.NET DateTime类的ToString行为不同。

3.    MySqlDateTime类支持NULL日期,但.NET DateTime类不支持NULL日期。如果未首先检查NULL,在试图将MySQLDateTime转换为DateTime时,会导致错误。

由于存在上述已知事宜,最佳建议仍是,在你的应用程序中仅使用有效日期。

26.2.4.7.5. 处理NULL日期

.NET DateTime数据类型不能处理NULL值。同样,在查询中为DateTime变量赋值时,必须首先检查值是否是NULL。

使用MySqlDataReader时,在赋值前,应使用.IsDBNull方法检查值是否为NULL:

[VB]

If Not myReader.IsDBNull(myReader.GetOrdinal("mytime")) Then
    myTime = myReader.GetDateTime(myReader.GetOrdinal("mytime"))
Else
    myTime = DateTime.MinValue
End If
  

[C#]

if (! myReader.IsDBNull(myReader.GetOrdinal("mytime")))
    myTime = myReader.GetDateTime(myReader.GetOrdinal("mytime"));
else
    myTime = DateTime.MinValue;
  
NULL值能够在数据集中使用,也能将其绑定以构成控件,无需特殊处理。
关注编程学问公众号