DBConnection
- 與 Connecton 相關的物件,最主要的功能就是移動「Disconnected 相關物件」與「資料儲存區」間的資料。
- 不同的資料儲存區,會使用不同的資料提供者,如 SqlClientDataProvider 、 OleDbDataProvider 、 OracleClientDataProvider 。
- 要建立一個連線,必須要有一個有效的連線字串。
- 傳送指令到資料儲存區前,必須開啟連線。
- 使用 Using 區塊可以在程式碼結束時,自動處置連線。
- 若使用同一個連線字串建立連線時,系統會使用 ThreadPool 中的連線物件,減少另外建立連線的資源消耗。
protected SqlConnection CreateConnection()
{
SqlConnection connection = new SqlConnection();
connection.ConnectionString =
@"
Data Source = VITO-2011W7\W7_SQLSVR_2008;
Initial Catalog = TestDB;
User ID=vito;
Password=vito1234;
";
return connection;
}
protected void btnCreateConnection_Click(object sender, EventArgs e)
{
SqlConnection connection = CreateConnection();
try
{
connection.Open();
}
catch (Exception ex)
{
myDebug.Write(ex.ToString());
}
finally
{
connection.Close();
}
}
DbCommand
- DbCommand 可以傳送一個或多個結構化的查詢語言。
- DbCommand 必須透過一個開啟的連線,才能連結到資料儲存區。
- DbCommand 必須指明 CommandText 及 CommandType 屬性, CommandType 屬性預設為 Text 。
- DbCommand 可以透過 DbParameter 物件傳遞參數資料。
建立 DbCommand
SqlCommand oCmd = oConn.CreateCommand(); oCmd.CommandType = CommandType.Text; oCmd.CommandText = "SELECT top 100 * From tblUser";
執行 DbCommand
- ExecuteNonQuery :執行 SQL 陳述式,不傳回值。
- ExecuteScalar :執行 SQL查詢陳述式,並傳回查詢結果集中第一個資料列的第一個資料行。
- ExecuteReader :執行 SQL 陳述式,並傳回 DbDataReader。
using (SqlConnection oConn = CreateTestDBConnection())
{
oConn.Open();
//===========================================================
// ExecuteNonQuery
//===========================================================
DbCommandoCmd1 = oConn.CreateCommand();
oCmd1.CommandType = CommandType.Text;
oCmd1.CommandText = "Insert Employee(Name,Salary) Values ('vito','1000')";
oCmd1.ExecuteNonQuery();
//===========================================================
// ExecuteScalar
//===========================================================
DbCommandoCmd2 = oConn.CreateCommand();
oCmd2.CommandType = CommandType.StoredProcedure;
oCmd2.CommandText = "usp_InsertEmployee";
int id = Convert.ToInt32( oCmd2.ExecuteScalar() );
//===========================================================
// ExecuteReader
//===========================================================
DbCommandoCmd3 = oConn.CreateCommand();
oCmd3.CommandType = CommandType.Text;
oCmd3.CommandText = "SELECT top 100 * From tblUser";
DbDataReader oReader = oCmd3.ExecuteReader();
}
}
使用 DbDataReader 物件
- 表示伺服器端(server-side)資料指標。
- 必須由 ExecuteReader 回傳,不能自已建立。
- 只能順向唯讀。(forward-only & read-only)
- 透過 Read 方法,可以擷取資料至緩衝區。但一次僅能處理一個資料列。
- 使用 DataReader 時,相關聯的 Connection 會忙於服務 DataReader,所以無法在相同 Connection 上再執行其他作業,直到 DataReader 關閉為止。
SqlCommand oCmd = oConn.CreateCommand();
oCmd.CommandType = CommandType.Text;
oCmd.CommandText = "SELECT top 10000 * From tblUser";
oCmd.Connection = oConn;
oConn.Open();
oReader = oCmd.ExecuteReader();
//順向逐筆讀取
while (oReader.Read())
{
if (oReader["UserName"].ToString() == "vito")
{
myDebug.WriteLine("Find");
break;
}
}
//將 DataReader 轉成 DataTable
DataTable dTable = new DataTable();
dTable.Load(oReader);
使用多重作用結果集 ( Multiple Active Result Sets , MARS )
- 通常在建立伺服器指標之後,若這個指標沒有關閉,在同一個連線之下,就無法再執行任何 Command 。
- 也就是若建立的 SqlDataReader 沒有關閉前,就無法再對資料庫下命令。
- 不過,從 Server2005 之後,開始支援一個連線多個指令,搭配連線字串的設定,即可啟用就 MARS。
- 但是 MARS 僅能簡化設計的工作量,並不能增加系統效能,反而會增加系統的負載量,所以請勿隨意使用。
protected SqlConnection CreateMARSConnection()
{
SqlConnection connection = new SqlConnection();
connection.ConnectionString =
@"
Data Source = VITO-2011W7\W7_SQLSVR_2008;
Initial Catalog = TestDB;
User ID = vito;
Password = vito1234;
MultipleActiveResultSets = True;
";
return connection;
}
protected void btnMARS_Click(object sender, EventArgs e)
{
string UserID = "";
string UserName = "";
using (SqlConnection oConn = CreateMARSConnection())
{
oConn.Open();
SqlCommand oCmd1 = new SqlCommand("SELECT top 10 UserID From tblUser", oConn);
SqlDataReader Reader1 = oCmd1.ExecuteReader();
SqlCommand oCmd2 = new SqlCommand();
oCmd2.Connection = oConn;
oCmd2.CommandText = "SELECT UserName From tblUser Where UserID = @UserID";
while (Reader1.Read())
{
UserID = Reader1["UserID"].ToString();
oCmd2.Parameters.Clear();
oCmd2.Parameters.Add(new SqlParameter("@UserID", UserID));
UserName = oCmd2.ExecuteScalar().ToString();
//若沒有啟用 MARS ,會產生「已經開啟一個與這個 Command 相關的 DataReader,必須先將它關閉。」
}
}
}
使用 SqlBulkCopy 物件執行大量複製作業
SqlBulkCopy 這個類別提供多個 WriteToServer 多載方法,分別是針對不同型態的資料進行複製作業。它是一個高效率的資料匯入方法,但匯入對象僅限 SQL Server 。
public void WriteToServer(DataRow[] rows); public void WriteToServer(DataTable table); public void WriteToServer(IDataReader reader); public void WriteToServer(DataTable table, DataRowState rowState);
protected void btnSqlBulkCopy_Click(object sender, EventArgs e)
{
using (SqlConnection oConn = CreateTestDBConnection())
{
oConn.Open();
SqlCommand oCmd = oConn.CreateCommand();
oCmd.CommandType = CommandType.Text;
oCmd.CommandText = "SELECT top 10000 UserName From tblUser";
//建立目標資料庫的連線
SqlConnection bulkCopyConnection = CreateTestDBConnection();
bulkCopyConnection.Open();
//執行 BulkCopy
SqlDataReader rdr = oCmd.ExecuteReader();
SqlBulkCopy bc = new SqlBulkCopy(bulkCopyConnection);
bc.DestinationTableName = "tblUserName";
bc.WriteToServer(rdr);
//關閉連線
bulkCopyConnection.Close();
}
}
DbDataAdapter
若要執行 SQL 指令,你只要建立 DbCommand 物件即可。 若想取得執行結果,也可以叫用 DbCommand 的 ExecuteReader 方法, 但這個方法回傳的資料是一個 DbDataReader 物件,它是一個「連線狀態」的物件,也就是在讀取資料時,必須與資料來源一直保持連接的狀態。
若想要更進一步的操作資料庫,就必須藉由 DataAdapter 來幫助。 DataAdapter 是由一個 Connection 和數個 SQL Commnad 所組合成的物件, DataAdapter 是採用離線存取的方式, 在設定好想關資訊後,就可以透過 Fill 方法去取得資料,它會自動開啟連線,並且在撈完資料後關閉連線。 你可以藉由它將查詢結果填入 DataSet 或 DataTable 等離線狀態的物件。 也可以利用它將異動後的資料更新回資料庫去。

SqlDataAdapter
建構子
public SqlDataAdapter(); public SqlDataAdapter(SqlCommand selectCommand); public SqlDataAdapter(string selectCommandText, SqlConnection selectConnection); public SqlDataAdapter(string selectCommandText, string selectConnectionString);
屬性
DbDataAdapter 物件中包含了 SelectCommand、InsertCommand、UpdateCommand、DeleteCommand 四種屬性,用來擷取或更新資料庫由的資料。
- SelectCommand :a command used to select records in the data source.
- InsertCommand :a command used to insert new records into the data source.
- UpdateCommand :a command used to insert update records into the data source.
- DeleteCommand :a command used to insert delete records into the data source.
- UpdateBatchSize :啟用或停用批次處理。
- 1:停用批次。此為預設值,將變更的資料列一列一列的回傳處理。
- 0:沒有限制批次大小。
- n:每次以 n 筆資料批次傳送處理。 (n>1)
Fill 方法
Fill 方法是用來將資料由資料庫移到指定的物件。 它會依據 DbDataAdapter 中的 SelectCommand 中的指令,將資料是由資料庫載入到 DataTable 之中。
public override int Fill(DataSet dataSet); public int Fill(DataTable dataTable); public int Fill(DataSet dataSet, string srcTable); protected virtual int Fill(DataTable dataTable, IDbCommandcommand, CommandBehavior behavior); ......
protected void btnFill_Click(object sender, EventArgs e)
{
using (SqlConnection connection = CreateTestDBConnection())
{
connection.Open();
SqlCommand cmd = connection.CreateCommand();
cmd.CommandText = "SELECT UserID, UserName FROM tblUser";
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataSet dsUsers = new DataSet("Users");
adapter.Fill(dsUsers, "Users"); //將查詢結果填到DataSet
Tools.bindDataToGridView(gv1, dsUsers, "Users");
}
}
Update 方法
public int Update(DataRow[] dataRows); public override int Update(DataSet dataSet); public int Update(DataTable dataTable); protected virtual int Update(DataRow[] dataRows, DataTableMapping tableMapping); public int Update(DataSet dataSet, string srcTable);
Update 方法是用來將變更的資料回存到資料庫。 它會依據 DataRow 的狀態值判斷,只要 DataRow.RowState 值不是 Unchanged ,才會進行傳送。 也就是它只傳送異動過的資料回資料庫進行更新處理。
使用 Update 方法時,原則上必須先設定好 DbDataAdapter 物件的四個指令屬性 (SelectCommand、InsertCommand、UpdateCommand、DeleteCommand),系統才會知道應該如何回存資料。 不過,若針對單一資料表,可以使用 DbCommandBuilder 物件,它為根據 SelectCommand 指令物件內容,快速的自動建立好其他三種指令物件。
protected void btnUpdate_Click(object sender, EventArgs e)
{
using (SqlConnection connection = CreateTestDBConnection())
{
connection.Open();
SqlCommand cmdSelect = connection.CreateCommand();
cmdSelect.CommandText = "SELECT Top 10 UserID, UserName FROM tblUser";
//設定 SelectCommand
SqlDataAdapter adapter = new SqlDataAdapter(cmdSelect);
//建立 SqlCommandBuilder
SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(adapter);
//將查詢結果填到DataSet
DataSet dsUsers = new DataSet("Users");
adapter.Fill(dsUsers, "Users");
Tools.bindDataToGridView(gv1, dsUsers, "Users");
//測試更新資料
dsUsers.Tables[0].Rows[0]["UserName"] = dsUsers.Tables[0].Rows[0]["UserName"].ToString() + "1";
adapter.Update(dsUsers, "Users");
//原本應該提供一個 DbCommand給 UpdateCommand 屬性
//不過這個簡單例子,我們可以使用 SqlCommandBuilder 幫忙即可
//底下檢視一下,這些自動建立的 DbCommand的樣子
myDebug.WriteLine(cmdBuilder.GetInsertCommand().CommandText); //該內容是在第14行指令後才建出來的
myDebug.WriteLine(cmdBuilder.GetUpdateCommand().CommandText);
myDebug.WriteLine(cmdBuilder.GetDeleteCommand().CommandText);
//INSERT INTO [Products] ([ProductName]) VALUES (@p1)
//UPDATE [Products] SET [ProductName] = @p1 WHERE (([ProductID] = @p2) AND ([ProductName] = @p3))
//DELETE FROM [Products] WHERE (([ProductID] = @p1) AND ([ProductName] = @p2))
}
}
批次更新
當執行 Update 方法,系統是將已變更的資料列,一列一列 (row-by-row) 的傳回資料庫以進行更新,若想增加效能,可以設定 UpdateBatchSize 屬性,將傳送筆數增加。 UpdateBatchSize 屬性預設值為1,若設成 0 表示沒有限制。
If the RowState is anything but Unchanged , the Update method sends the change to the database.
RowUpdateEvent 事件
這個事件發生在 Update 方法執行之後,我們可以訂閱這個事件,來檢測批次更新是否有一次傳送多筆資料。
protected void btnBatch_Click(object sender, EventArgs e)
{
using (SqlConnection connection = CreateTestDBConnection())
{
SqlCommand cmdSelect = connection.CreateCommand();
cmdSelect.CommandType = CommandType.Text;
cmdSelect.CommandText = "SELECT Top 10 UserID, UserName FROM tblUser";
SqlDataAdapter da = new SqlDataAdapter(cmdSelect);
SqlCommandBuilder bldr = new SqlCommandBuilder(da);
DataSet dsProducts = new DataSet();
connection.Open();
//==========================================================
// Fill Data
//==========================================================
da.Fill(dsProducts);
Tools.bindDataToGridView(gv1, dsProducts);
//==========================================================
// Update Data (共3筆)
//==========================================================
dsProducts.Tables[0].Rows[0]["UserName"] = System.DateTime.Now.ToString();
dsProducts.Tables[0].Rows[1]["UserName"] = System.DateTime.Now.ToString();
dsProducts.Tables[0].Rows[2]["UserName"] = System.DateTime.Now.ToString();
// handle RowUpdated Event
da.RowUpdated += SqlRowUpdated;
da.UpdateBatchSize = 2;
da.Update(dsProducts);
}
}
//這個事件會發生二次
//第一次 RecordsAffected=2
//第一次 RecordsAffected=1
protected void SqlRowUpdated(object sender, SqlRowUpdatedEventArgs e)
{
DataRow dr = e.Row;
myDebug.WriteLineBR(e.RecordsAffected.ToString());
}
DbProviderFactory
什麼是 DbProviderFactory 類別
DbProviderFactory 類別是建立 DataProvider 的實作,透過它可以用來動態指定資料庫提供者。
DbProviderFactory 類是 ADO.NET 中的一個 DBProvider 的基底類別,各種特定的 DBProvider 都繼承它,它的架構就像 Design Pattern 中的 Simple Factory 。 我們可以利用這個類別,在程式中不直接指定使用哪一個 DBProvider ,再利用設定檔中設定,然後於執行階段時再決定。
public abstract class DbProviderFactory
{
protected DbProviderFactory();
public virtual DbConnection CreateConnection();
public virtual DbDataAdapter CreateDataAdapter();
public virtual DbCommandCreateCommand();
public virtual DbCommandBuilder CreateCommandBuilder();
public virtual DbParameter CreateParameter();
public virtual DbConnectionStringBuilder CreateConnectionStringBuilder();
public virtual DbDataSourceEnumerator CreateDataSourceEnumerator();
public virtual CodeAccessPermission CreatePermission(PermissionState state);
}

建立 DbProviderFactory 類別
SqlConnection connection = new SqlConnection(); connection.ConnectionString = ConfigurationManager.ConnectionStrings["NorthWindConnectionString"].ConnectionString; connection.Open(); connection.Close();
string InvariantName = "System.Data.SqlClient"; DbProviderFactory dbFactory = DbProviderFactories.GetFactory(InvariantName); DbConnection connection = dbFactory.CreateConnection(); connection.ConnectionString = ConfigurationManager.ConnectionStrings["NorthWindConnectionString"].ConnectionString; connection.Open(); connection.Close();
例外狀況的處理
protected void btnExceptionHandle_Click(object sender, EventArgs e)
{
using (SqlConnection oConn = CreateTestDBConnection())
{
try
{
SqlCommand oCmd = oConn.CreateCommand();
oCmd.CommandText = "RaisError('Custom Error',18,1)";
oConn.Open();
oCmd.ExecuteNonQuery();
}
catch (DbException ex)
{
foreach (DictionaryEntry item in ex.Data)
{
myDebug.WriteLine(item.Value.ToString());
}
}
}
}
ADO.NET 常見的 DB 存取錯誤訊息
下列幾項是程式更新資料庫時常會出現的錯誤訊息。
1. 當傳遞擁有已修改資料列的 DataRow 集合時,更新需要有效的 UpdateCommand。
當呼叫 DataAdapter 的 Update 方法時,如果沒有指定適當的 UpdateCommand 物件給 DataAdapter 的話就會發生這個錯誤。 最簡單的解法就是使用 CommandBuilder 來自動產生相對應的 Insert, Delete, Update SQL 語法。
private void GetData()
{
String strConn = ConfigurationManager.ConnectionStrings["EmpConnectionString"].ConnectionString;
SqlConnection objConn = new SqlConnection(strConn);
SqlCommand objCmd = new SqlCommand();
objCmd.Connection = objConn;
objCmd.CommandText = "SELECT ID, Name, Age FROM Emp";
DataTable dt = new DataTable();
objAdapter.SelectCommand = objCmd;
objAdapter.Fill(dt);
dataGridView1.DataSource = dt;
}
private void btnUpdate_Click(object sender, EventArgs e)
{
//使用 CommandBuilder 來產生相對應的 Insert, Delete, Update SQL語法
var cmdBuilder = new SqlCommandBuilder();
cmdBuilder.DataAdapter = objAdapter;
objAdapter.Update(((DataTable)dataGridView1.DataSource));
}
2. 並行違規: UpdateCommand 已經影響必須是 1 記錄的 0。
這個錯誤會發生在同時有兩個人在編輯一筆資料的時候,若第一個人先叫用了 Update ,第二個人若再叫用 Update 就會產生「並行違規: UpdateCommand 已經影響必須是 1 記錄的 0。」的錯誤。 (Concurrency violation: the UpdateCommand affected 0 of the expected 1 records.) 也就是說,程式碼預期資料庫應該會回應 1 筆資料被更新,但是卻回應 0 筆資料被更新,這是不對的,所以產生該項錯誤。
之所以會這個樣子,是因為使用了 SqlCommandBuilder 自動產生的更新命令,不管資料是否包含鍵值,這個更新命令都會更據最後一次 AcceptChanges 時的所有欄位的資料當做 Where 條件去執行變更。例如:
private void btnUpdate2_Click(object sender, EventArgs e)
{
int idx = dt.Rows.Count - 1;
// 若該筆資料一開始為 (ID,Name,Age) = (1, "AAA", 20)
dt.Rows[idx]["Name"] = "BBB";
dt.AcceptChanges(); // 叫用 AcceptChanges 後,此時資料變更為 (ID,Name,Age) = (1, "BBB", 20)
dt.Rows[idx]["Name"] = "CCC";
objAdapter.Update(((DataTable)dataGridView1.DataSource));
// 當叫用 Update 時,SqlCommandBuilder 自動產生的更新語法為
// UPDATE EMP SET Name='CCC' WHERE ID=1 AND NAME='BBB' AND Age=20 (更據所有欄位值去當成 where 條件)
// 這個語法實際對資料庫操作時,會回傳 0 筆被更新,這與預期不符,所以就產生錯誤。
// 如果資料庫中剛好有一筆符合的資料,那麼就會更新到非預期的資料,但不會產生錯誤。
}
3. 不會傳回任何重要資料行資訊的 SelectCommand 不支援 UpdateCommand 動態 SQL 的產生。
Error: Dynamic sql generation for the update command is not supported against a select command that does not return any key column information
這個錯誤發生在 SELECT 出來的資料沒有包含主鍵,所以無法動態產生 Update 指令。加入Primary Key 就可以了。

偵測連接事件的資訊
Connection 物件包含一個 InfoMessage 事件,可以用來擷取由資料庫回應出來的一般資訊或錯誤資料。
protected void ConnectionInfoMessage(object sender, SqlInfoMessageEventArgs e)
{
foreach (SqlError err in e.Errors)
{
if (err.Class > 9) // Severity
{
Console.WriteLine("Error Message : {0} : State : {1}", err.Message, err.State);
}
else
{
Console.WriteLine("Info Message : {0} : State : {1}", err.Message, err.State);
}
}
}
protected void btnInfoMessage_Click(object sender, EventArgs e)
{
using (SqlConnection connection = CreateTestDBConnection())
{
connection.InfoMessage +=
new SqlInfoMessageEventHandler(ConnectionInfoMessage);
connection.Open();
DbCommandoCmd1 = connection.CreateCommand();
oCmd1.CommandType = CommandType.StoredProcedure;
oCmd1.CommandText = "usp_Test";
oCmd1.ExecuteNonQuery();
}
}
使用 Transaction 物件
protected void btnTransation_Click(object sender, EventArgs e)
{
using (SqlConnection connection = CreateTestDBConnection())
{
connection.Open();
SqlTransaction transaction;
transaction = connection.BeginTransaction();
try
{
SqlCommand cmd = new SqlCommand();
//必須將 Connection 和 Transaction 二個物件都指定給 Command 物件
cmd.Connection = connection;
cmd.Transaction = transaction;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "usp_Test";
cmd.ExecuteNonQuery();
cmd.CommandText = "usp_Test2";
cmd.ExecuteNonQuery();
transaction.Commit(); //commit the transaction
}
catch
{
transaction.Rollback(); //rollback the transaction
}
}
}
非同步資料存取 ( Asynchronous Data Access)
使用非同步資料存取功能時,必須在連線字串中,設定 Asynchronous Processing = true ,並在程式中叫用以下非同的執行方法。
public IAsyncResult BeginExecuteNonQuery(); public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject); public IAsyncResult BeginExecuteReader(); public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject); public IAsyncResult BeginExecuteXmlReader(); public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateObject);
protected void btnAsynchronous_Click(object sender, EventArgs e)
{
// 必須在連線設定中指定 Asynchronous Processing = true
string connString = @"
Data Source = VITO-2011W7\W7_SQLSVR_2008; Initial Catalog = Vito2012; User ID=vito; Password=vito1234;
Asynchronous Processing = true;
MultipleActiveResultSets = true";
SqlConnection conn1 = new SqlConnection(connString);
SqlConnection conn2 = new SqlConnection(connString);
SqlConnection conn3 = new SqlConnection(connString);
SqlCommand cmd1 = conn1.CreateCommand();
cmd1.CommandType = CommandType.StoredProcedure;
cmd1.CommandText = "usp_AsysTest";
SqlCommand cmd2 = conn2.CreateCommand();
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.CommandText = "usp_AsysTest";
SqlCommand cmd3 = conn3.CreateCommand();
cmd3.CommandType = CommandType.StoredProcedure;
cmd3.CommandText = "usp_AsysTest";
DateTime dt1 = DateTime.Now;
conn1.Open();
conn2.Open();
conn3.Open();
IAsyncResult result1 = cmd1.BeginExecuteNonQuery();
IAsyncResult result2 = cmd2.BeginExecuteNonQuery();
IAsyncResult result3 = cmd3.BeginExecuteNonQuery();
//result1.AsyncWaitHandle.WaitOne();
//result2.AsyncWaitHandle.WaitOne();
//result3.AsyncWaitHandle.WaitOne();
cmd1.EndExecuteNonQuery(result1);
cmd2.EndExecuteNonQuery(result2);
cmd3.EndExecuteNonQuery(result3);
DateTime dt2 = DateTime.Now;
TimeSpan timespan1 = dt2 - dt1;
myDebug.WriteLineBR(timespan1.ToString()); // 00:00:05.0012860
conn1.Close();
conn2.Close();
conn3.Close();
}
如何使用大型二進位物件 (Binary Large OBject, BLOB)
什麼是大型二進位物件
在 SQL 中,有以下幾種比較大型的欄位型態:
| 型別 | Unicode | 長度 | 最大長度 |
|---|---|---|---|
| image | 可變 | 2^31-1 | |
| binary | 固定 | 8,000 | |
| varbinary | 可變 | 2^31-1 | |
| text | 可變 | 2^31-1 | |
| ntext | Unicode | 可變 | 2^30-1 |
| varchar | 固定 | 8,000 | |
| nvarchar | Unicode | 可變 | 2^31-1 (2GB) |
因為 MS 將在未來的版本中,移除 ntext、text 和 image 等資料類型。 所以請避免在新的開發工作中使用這些資料類型。 可改用 nvarchar(max)、varchar(max) 和 varbinary(max) 取代。
更多說明,可參考MSDN:
寫入 BLOG 資料
protected void btnWriteBLOB_Click(object sender, EventArgs e)
{
string sFilePath = @"D:\VitoBlogPic\test1.jpg";
byte[] photo = GetPhoto(sFilePath);
using (SqlConnection oConn = CreateTestDBConnection())
{
SqlCommand oCmd = oConn.CreateCommand();
oCmd.CommandType = CommandType.Text;
oCmd.CommandText = "INSERT Employee(Name,Picture) Values (@Name, @Picture)";
oCmd.Parameters.Add("@Name", SqlDbType.NVarChar, 150).Value = "vito";
oCmd.Parameters.Add("@Picture", SqlDbType.VarBinary, photo.Length).Value = photo;
try
{
oConn.Open();
oCmd.ExecuteNonQuery();
}
catch (Exception ex)
{
myDebug.WriteLine(ex.Message);
}
}
}
public static byte[] GetPhoto(string filePath)
{
FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
BinaryReader reader = new BinaryReader(stream);
byte[] photo = reader.ReadBytes((int)stream.Length);
reader.Close();
stream.Close();
return photo;
}
讀取 BLOG 資料
protected void btnReadBLOB_Click(object sender, EventArgs e)
{
using (SqlConnection oConn = CreateTestDBConnection())
{
SqlCommand oCmd = oConn.CreateCommand();
oCmd.CommandType = CommandType.Text;
oCmd.CommandText = "Select * from Employee where EmployeeID='9411993'";
try
{
oConn.Open();
SqlDataReader reader = oCmd.ExecuteReader();
if (reader.Read())
{
byte[] photo = reader["Picture"] as byte[];
string sFilePath = @"C:\EmployeePic.jpg";
FileStream fs = new FileStream(sFilePath, FileMode.OpenOrCreate, FileAccess.Write);
fs.Write(photo, 0, photo.Length);
fs.Close();
}
}
catch (Exception ex)
{
myDebug.WriteLine(ex.Message);
}
}
}
上面例子會將資料庫中的資料讀取後儲存成檔案,若我們處理的 BLOB 是圖檔資料,我們可以建立一個 「泛型處理常式」來處理網頁讀取圖檔的需求,以方便將圖型資料顯示在網頁上。
1) 假設我們在網頁上放置了一個 Image 控制項
<asp:Image ID="Image1" runat="server" />
2) 我們可以在 Code Behind 中,指定要顯示的資料
Image1.ImageUrl = @"../Common/Image.ashx?EmpID=9411993";
3) 然後撰寫一個泛型處理常式
public class Image : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string EmpID = context.Request.QueryString["EmpID"];
string ConStr = WebConfigurationManager.ConnectionStrings["TestDBConnectionString"].ConnectionString;
using (SqlConnection oConn = new SqlConnection(ConStr))
{
SqlCommand oCmd = oConn.CreateCommand();
oCmd.CommandType = CommandType.Text;
oCmd.CommandText = "Select Picture from Employee where EmployeeID=@EmpID";
oCmd.Parameters.Add(new SqlParameter("@EmpID", EmpID));
try
{
oConn.Open();
SqlDataReader reader = oCmd.ExecuteReader();
if (reader.Read())
{
if (!Convert.IsDBNull(reader["Picture"]))
{
byte[] photo = reader["Picture"] as byte[];
context.Response.ContentType = "image/jpeg";
context.Response.BinaryWrite(photo);
}
else
context.Response.Write("./images/noimage.gif");
}
}
catch (Exception ex)
{
myDebug.WriteLine(ex.Message);
}
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
沒有留言:
張貼留言