顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

2012年7月5日 星期四

C# 資料庫存取

首先關於資料庫的開啟可考慮兩種方式
1.用using{...}區塊
using(SqlConnection con = new SqlConnection())
{
    con.ConnectionString = "....";
    con.Open();
    ....
}

這種方法會在脫離區塊後立刻釋放Connection物件的資源
所以不用使用資料庫的關閉方法(如Close()、Dispose())
另外就是正規的寫法

2.

using System.Data.SqlClient;
....
SqlConnection con = new SqlConnection("xxx");       //xxx = connection string
SqlCommand cmd = new SqlCommand("xxx", con); //xxx = SQL command 
SqlDataReader dr;
...
con.Open();
dr = cmd.ExecuteReader();    //使用DataReader物件讀取資料庫內容
...
con.Close();  


SqlCommand物件的幾個常用方法跟屬性
   ExecuteNonQuery() -
   可以執行INSERT、UPDATE、DELETE等SQL command,成功時回傳受影響的紀錄筆數

   ExecuteScalar() -
   適用於結果集只回傳一個資料列的第一欄,例如SQL command 的COUNT()、MAX()等

   ExecuteReader() -
   執行Command物件中所指定的SQL的SELECT敘述,並建立一個DataReader來瀏覽資料

   CommandText -
   設定或取得要執行的命令


SqlCommand使用範例:

string sqlStr = string.Empty;
sqlStr = "INSERT INTO TableA(姓名, 聯絡方式, 薪水)" + "VALUES(@name, @tel, @pay)"
SqlCommand cmd = new SqlCommand( sqlStr , con);
cmd.CommandType = CommandType.StoreProcedure;     //預先編譯SQL處理函式

cmd.Parameters.Add(new SqlParameter("@name", SqlDbType.NVarChar));
cmd.Parameters.Add(new SqlParameter("@tel", SqlDbType.NVarChar));
cmd.Parameters.Add(new SqlParameter("@pay", SqlDbType.NVarChar));
cmd.Parameters["@name"].Value = "vc56";
cmd.Parameters["@tel"].Value = "0000000";
cmd.Parameters["@pay"].Value = "$20000";

cmd.ExecuteNonQuery();


DataReader有幾個方法跟屬性較為重要,以下列出:
   FieldCount屬性 -
   將已查詢欄位的總數傳回
 
   Read() -
   將指標移到下一筆,並判斷是否指到EOF
   如果到資料結尾則回傳false

   Item[i] -
   取得第i欄的資料內容

   Item["欄位名稱"]集合 -
   取得指定欄位名稱所指的資料內容

   GetValue(i) -
   取得第i欄位的資料內容,傳回值為object資料型別

   IsDBNull(i) -
   判斷第i個欄位使否為資料庫Null值

使用範例:
while(dr.Read())
{
  for(int i = 0; i < dr.FieldCount; i++)
  {
      textBox1.Text += dr[i].ToString() + "\t";
  }
}

以執行效率來說,使用索引會比使用欄位名稱還快
此外,取出時可使用方法來省略手動轉型的動作,如下:

while(dr.Read())
{
  textBox1.Text += dr.GetString(0) + "\t";
  textBox1.Text += dr.GetInt32(1).ToString() + "\t";
  ...
}


DataSet則是ADO.NET的特色
他會先將資料庫的內容存至記憶體中,並得以執行離線操作
操作完畢後再將內容回寫至資料庫中
適用於多用戶端資料存取,當然占用的記憶體多是不可避免的事
DataSet中可以包含一個以上的DataTable物件
由DataAdapter使用Command物件執行SQL command
再將取得的資料寫至DataSet,如此就能用DataTable存取資料表,範例:

using System.Data.SqlClient;
using System.Data;

.....
using(SqlConnection con = new SqlConnection())
{
    con.ConnectionString = "....";
    DataSet ds = new DataSet();
    SqlDataAdapter dsContent = new SqlDataAdapter("xxx", con);  //xxx = sql command
    dsContent.Fill(ds, "Content");    //使用Fill()時,DataAdapter會自動連線到資料庫
    /*
    Adapter也可以改成SqlDataAdapter dsContent = new SqlDataAdapter();
                                     dsContent.SelectCommand = cmd;
    這樣就可以使用SqlCommand物件來保持彈性


    另外有幾種特性語法示範
   int n = ds.Tables.Count;                          //取得DataSet中DataTable的總數
   String tName = ds.Table[i].TableName;  //取得第i個DataTable的表格名稱
   */
   DataTable dt = ds.Tables["Content"];     //對應SqlDataAdapter  Fill ()時填入的資料表名
   for(int i = 0; i < dt.Rows.Count; i++)
   {
        for(int j = 0; j < dt.Columns.Count; j++)
        {
              textBox1.Text += dt.Rows[i][j].ToString() + "\t";
        }
        textBox1.Text += Enviroment.NewLine;
   }
}
....


DataSet另外尚有使資料表之間產生關連的操作

ds.Realations.Add(  "關聯名稱",
      ds.Tables["dt1"].Columns["dt1要關聯的欄位名稱"],
      ds.Tables["dt2"].Columns["dt2要關聯的欄位名稱"],
)

日後可與DataView物件結合使用
dataGridView1.DataSource = ds;
dataGridView1.DataMember = "dt1";

dataGridView2.DataSource = ds;
dataGridView2.DataMember = "dt1.關聯名稱";

2012年7月3日 星期二

[轉貼] C# 淺析SqlConnection的dispose和close方法差異

原文

引用微軟ADO.Team的經理的話說,sqlconnection的close和dispose實際是做的同一件事,
唯一的區別是Dispose方法清空了connectionString,即設置為了null.

SqlConnection con = new SqlConnection("Data Source=localhost;Initial Catalog=northwind;User ID=sa;Password=steveg");
        con.Open();
        con.Close();
        con.Open();
        con.Dispose();
        con.Open();

    上例運行發現,close掉的connection可以重新open
    dispose的不行,因為connectionstring清空了,
    會拋出InvalidOperationException提示The ConnectionString property has not been initialized,
    但請注意此時sqlconnection對象還在。

    如果dispose後給connectionString重新賦值,則不會報錯。 
    由此得出的結論是不管是dispose還是close都不會銷毀對象,即不會釋放內存,
    它們會把sqlconnection對象丟到連接池中,那此對象什麼時候銷毀呢?
    我覺得應該是connection timeout設置的時間內,
    如果程序中沒有向連接池發出請求說要connection對象,sqlconnection對象便會銷毀,
    這也是連接池存在的意義。

    剛開始以為dispose會釋放資源清空內存,
    如果這樣的話,連接池不是每次都是要創建新對象,那何來重用connection呢?
    在網上看到很多人說close比dispose好,
    我想真正的原因是dispose後的sqlconnection對象要重新初始化連接字符串而已,
    並不是像某些人說的dispose會釋放對象。

    所以在try..catch和using的選擇上大膽的使用using吧,
    真正的效率差異我想可能只有百萬分之一秒吧
    (連接池重用該連接對象初始化連接字符串的時間),
    而且enterprise library中封裝的data access層全是用的using,
    從代碼的美觀和效率上綜合考慮,using好 

    補充:
    using不會捕捉其代碼快中的異常,
    只會最後執行dispose方法,相當於finally{dispose},
    本文主要是想說明dispose和close的差異,因為using是絕對dispose的,
    可是如果人為的寫try..finally有的人會選擇close有的人會選擇dispose,
    實際上在這2者的選擇上是有差異的。

    2012年7月1日 星期日

    C# 文字控制項-自動完成輸入功能

    C#的自動完成功能提供了當使用者在文字方塊中輸入的資料符合條件時
    便會自動填入或彈出符合的字串以提升使用者輸入資料的效率

    TextBox或ComboBox 中的三個屬性須注意:
    AutoCompleteSource、AutoCompleteMode、AutoCompleteCustomSource



    AutoCompleteSource的常用屬性值包括

    1.HistoryList - 以URL中的歷史清單作為自動完成輸入功能的來源
       ex:textBox1.AutoCompleteSource = AutoCompleteSource.HistoryList;
       程式碼部分以下類推

    2.RecentlyUsedList - 以URL中的歷史清單和最近瀏覽過的URL為來源

    3.AllUrl - 以最近瀏覽過的URL作為自動完成輸入功能的來源

    4.FileSystem - 以檔案系統為來源

    5.FileSystemDirectories - 以磁碟和目錄為來源,不包括檔案

    6.AllSystemSources - 以AllUrl和FileSystem為來源

    7.None - 取消自動完成輸入功能來源

    8.CustomSource - 自訂來源,須搭配AutoCompleteCustomSource屬性設定



    AutoCompleteMode常用屬性

    1.Append - 將最可能相符之候選字串其餘部分附加到現有字串之後,並反白顯示
       ex:textBox1.AutoCompleteMode = AutoCompleteMode.Append;

    2.Suggest - 挑選出最符合的字串於下拉選單的選項中

    3.SuggestAppend - 混用上述兩種功能

    4.None - 停用自動完成輸入功能


    AutoCompleteCustomSource屬性

    除了直接在Widget屬性介面上設計外
    也可以用程式碼部分撰寫,如:

    string[] content = new string[]{"a","b","c"};
    AutoCompleteStringCollection newAdd = new AutoCompleteStringCollection();
    newAdd.AddRange(content);

    textBox1.AutoCompleteMode = AutoCompleteMode.Suggest;
    textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
    textBox1.AutoCompleteCustomSource = newAdd;

    2012年6月27日 星期三

    [轉貼] C# Delegate/event 的簡單理解

    看這篇前可以先看這篇文章瞭解Delegate的語法與使用方式
    本篇原文=================
    簡單來說就是增加一層間接層來實現晚綁定,從而獲得更為鬆散的耦合

    為什麼要有Delegate? 
    假設有兩個不同類實現的對象A和B,A和B分別有方法a和b。現在需要a方法以一觸發,便執行對象B的方法b。 不使用delegate,我們可以在設計A類的時候在將B類作為參數傳入a方法,這樣可以實現我們的要求。但是這樣做有明顯的缺陷:   
        1. 我們需要事先知道B類。   
        2. 如果還存在C類、D類等的方法需要一併執行,這樣必須重新修改A類的代碼。 如果有了  Delegate,我們可以事先設計一個函數指針,這樣當對象A執行a方法的時候,就可以根據該指針指向的,符合該函數指針法則的方法進行執行。這樣,我們就不需要在設計A類的時候知道B類,只需要知道B的b方法的參數和返回值就可以了。如果還有C類、D類方法需要一併執行,也可以添加到該指針指向的數據結構中。


    示例代碼分析:
    代碼
        class Program
    {
    static void Main(string[] args)
    {
    A a
    = new A();
    B b
    = new B(a);
    a.Method_A();
    }
    }


    class A
    {
    public delegate void Delegate_A();
    public event Delegate_A da;
    public void Method_A()
    {
    da();
    }
    }

    class B
    {
    public B(A a)
    {
    a.da
    += new A.Delegate_A(Method_B);
    }
    public void Method_B()
    {
    Console.WriteLine(
    "B's method!");
    }
    }
    1. 先看Class A中   
    public delegate void Delegate_A(); 這個語句規定了函數指針指向的函數應該符合的規範:沒有返回值,沒有參數傳入。
    2. 接著實例化了一個 Delegate_A的對象da,這裡使用event關鍵字表明它是一個事件。 
    3. 再看Class B的的構造函數,可見B構造時,往對象a的事件中傳入了Method_B的函數地址(a.da += new A.Delegate_A(Method_B)
    4. 在Main函數中執行A對象的Method_A時,觸發了事件da,而da指向了B的方法。這樣即便A對象對B對象一無所知,也同樣能執行B的方法Method_B。這就是Delegate的好處。

    Event有什麼用:
    實際上在上例中將Event關鍵字刪除程序同樣能執行。之所以要加上Event,是因為在程序編譯時,加上Event以後該Delegate的對象會成為該類的私有成員,這樣外界只能通過 += 和 -=來對Delegate的對象da進行操作,而不能直接進行賦值。

    2012年6月26日 星期二

    C# 物件配置與記憶體議題

    對於實值型別(等同Java中稱的基本型別)一樣是放在stack
    在method中也同樣是以複製的形式傳遞
    也就是method中作的變動不會影響到原本的實值型別物件
    不過若要改變傳遞的形式則可以使用 ref、out 兩個關鍵字

    ref的用法為宣告使用call by reference
    如:public void swap(ref int x, ref int y){ ... //code for swap x&y }


    使用method時亦用ref,如:

    int a = 1;    int b = 2;
    swap(ref a, ref b);


    如此一來a與b的值就會在method呼叫過後改變了
    C#在配置物件(參考型別資料)時亦隱含了ref的宣告
    所以省略不寫也沒關係
    但若明白的了ref宣告
    則必須在參數、引述的地方都宣告,否則會引起編譯錯誤,如:

    public void dosth(ref int[,] intary){ .... }

    int[,] a = new int[]{{1,2},{3,4}}
    dosth(ref a);     //如果參數、引數只有其中一方宣告ref會引起錯誤


    out關鍵字的作用則是傳出值,如:
    public void assignValue(out int x, out int y){
        x = 6;  y = 10;                //必須在method中宣告值
    .........
    }

    int a; int b;                           //未賦予初始值
    assignValue(out a, out b);    //此時a = 6, b = 10


    不過用法雖然多樣化了,但似乎連Visual Studio Team也不鼓勵ref、out的使用
    畢竟若設計師不熟悉用法則容易出錯
    一般是建議找替代方法來達成目標

    struct的使用可以參考MSDN上的說明-使用結構
    使用上有無法繼承與多型的限制,但在僅用來儲存field時會有較class更好的效能
    其記憶體配置也是位在stack
    所以指派的時候也是使用複製,等同於創造出新的struct
    注意這點以避免疏失
    而struct作為參數傳遞時,可考慮使用ref,以優化性能(當然這時候要注意值的變化)

    C#的class有時候並不提供擴展,如第三方的類別等
    要封裝類別只要在class宣告前加入sealed關鍵字,如:sealed class ap{...}
    如果打算擴展這些類別,C#提供如下作法:

    static class secondAp{
        public static string getAns(this ap apobj, double fi1){...}
    }


    擴張方法必須是存在於靜態類別中的靜態方法
    this ap 指定擴充ap類別