2015年9月13日 星期日

物件Object的New,Dispose與Connection的Open,Close概念分享

什麼是New

我們經常可以看到有人使用一個物件的時候會寫

1Dim oEmp as ObjEmployee
2oEmp = New ObjEmployee
而且少了New就不能執行,那麼什麼是New呢 
New是產生執行個體,不過相信很多人又要問,什麼是【產生執行個體】 (能不能講明白一點啊!! )


這要提到電腦的程式運作過程。程式在電腦上跑,會把程式的區段,在記憶體裡面挖一塊空間,然後把程式碼載入那塊空間
接著程式在運作的時候,就是記憶體與CPU之間溝通、運算、記錄等動作,而把【挖塊記憶體,把程式載到那塊空間】這個過程就是【產生執行個體】。


因此沒有New的話會怎樣,程式區塊根本沒有載入到記憶體中,因此也無法運作執行
講的擬人化一點

Dim oEmp as ObjEmployee  '宣告oEmp是ObjEmployee這種物件類別(此時有魂無體)
oEmp = New ObjEmployee   '產生執行個體(此時投胎轉世,靈魂變成實體,可以開始工作)

什麼是 Dispose


當一個物件已經使用完後,未來不會再使用,此時這個物件已經完成他這一生(New完之後到現在)的任務。因此他可以塵歸塵、土歸土...一切還諸天地...此時就進行Dispose 
然而實際上,在記憶體中,當物件Dispose的時候,並沒有把該段記憶體清空,只是標註,這段空間不再使用,直到GC啟動把他清空才算真正的清空。


因此如果用完後,不進行 Dispose ...那麼就會等到該位置很久沒動作了(就像有個人躺在地上,已經超過一定時間(一個月??)沒動作,也沒呼吸),才會被其他的程式拿來用。
以上說明的是New(生)與 Dispose (死)的關係


什麼是Open


當你有了實體後,代表可以開始運作
但不是真的已經運作了
以Connecion來說
1Using Conn As New Connetion(ConnStr)
2    'Conn.Open
3    Dim Dr as SqlDataReader
4    Using Cmmd as New SqlCommand("Select...",Conn)
5        Dr = Cmmd.ExecuteReader
6    End Using
7End Using
上面這段程式會出錯,他告訴你...Connection沒有Open,也就例如您建立了一個橋,但是這座橋卻沒有通車,因此也沒辦法用。因此使用DataReader或者Cmmd.ExecuteNoneQuery的時候,都需要告訴他已經通車了...他才會正常運作。

而如果使用DataAdapter的話這個傢伙比較厲害... 他會看有沒有通車,有通車他就用;沒通車的話,他會自己把這通道打開,然後去使用。

最後說什麼是Close

Close是關閉通道,通道關了是可以再開的唷。
然後跟上面的 Dispose 再比對一下:
Close只是把通道關閉
 Dispose 卻是通知作業系統...我已經完成任務,這段記憶體你可以拿去再用...然後壽終正寢


垃圾回收機制

垃圾回收器有兩個基本的原理:

  1. 考慮某個物件在未來的程式執行中,將不會被存取。
  2. 向這些物件要求歸回記憶體。

下載與安裝MySQL資料庫


如果你已經安裝過MySQL資料庫和可以輸入和執行SQL敘述的軟體,接下來的內容就可以忽略,直接到第五節安裝範例資料庫就可以了。
MySQL的官方網站目前提供一個完整的安裝程式,在Windows平台只要下載與安裝一個檔案,就包含資料庫伺服器和所有需要的工具軟體,包含這裡需要使用的MySQL Workbench。你可以到這個連結準備開始下載:
進入這個網站以後,參考下面的說明,下載與儲存完整的安裝檔案:
mysql_02_snap_19
下載完成後,執行安裝程式,選擇開始安裝並同意版權聲明後,在選擇安裝種類的畫面選擇Developer Default:
mysql_02_snap_20
後面的步驟依照畫面的指示,選擇Execute或Next,就會進入開始安裝的步驟。安裝完成後,就可以準備進入設定MySQL資料庫的步驟:
mysql_02_snap_21
依照畫面的指示,選擇Next進入設定資料庫管理員(root)密碼的步驟,輸入一個你自己決定的密碼:
mysql_02_snap_22
依照畫面的指示,選擇Next完成設定資料庫的工作。在最後完成安裝與設定的步驟,勾選Start MySQL Workbench after Setup選項後,選擇Finish結束安裝與設定MySQL資料庫的工作。
mysql_02_snap_23
安裝程式會啟動MySQL Workbench,依照下面的說明,準備設定資料庫連線的基本資訊:
mysql_02_snap_24
選擇下面畫面說明的按鈕:
mysql_02_snap_25
在出現的對話框中輸入在安裝過程中決定的密碼:
mysql_02_snap_26
選擇Test Connection按鈕:
mysql_02_snap_27
如果出現這樣的畫面,表示可以正確的連線到MySQL資料庫:
mysql_02_snap_28
在MySQL Workbench主畫面選擇Connect:
mysql_02_snap_29
連線到資料庫後,在左側的World資料庫名稱上點兩下(Double click),會發現World會變成粗體字,表示目前開啟(作用中)的資料庫。在畫面中輸入一個測試的SQL敘述,SELECT * FROM country。輸入完後,按下執行敘述的快速鍵Ctrl + Enter,就可以看到所有的國家資料:
mysql_02_snap_30

5. 安裝範例資料庫

完成前面的安裝與設定工作後,MySQL資料庫伺服器中已經有一個內建的範例資料庫world,後面的文章會使用這個資料庫討論與說明一些主題。不過因為這個資料庫比較簡單一些,所以要請你安裝另外一個範例資料庫,後面的文章討論到一些不同的主題時,就會用到這個額外的範例資料庫。
在下面的連結按滑鼠右鍵後,選擇另存連結,下載與儲存一個建立資料庫的SQL Script檔案:
在MySQL Workbench中選擇File->Open SQL Script,選擇剛才下載與儲存的檔案,就可以看到像這樣的畫面:
MySQL_032
在MySQL Workbench中選擇Query->Execute(All or Selection),Workbench會花一點時間執行所有的敘述。執行完成後,在資料庫列表區塊的任何空白位置,按滑鼠右鍵後選擇Refresh All,就可以看到安裝好的新資料庫cmdev:
MySQL_033
在下面的連結按滑鼠右鍵後,選擇另存連結,下載與儲存另一個建立資料庫的SQL Script檔案,重複上面的步驟建立名稱為world的範例資料庫:
完成所有準備工作,下一篇文章就可以開始進入SQL的世界了。

2015年9月10日 星期四

jQuery自動完成懶人包

前陣子在專案中用了jQuery AutoComplete Plugin,感覺甚好,我想將來很可能會繼續用在其他專案上,索性做了一個範例懶人包以備未來不時之需,利人也利已。(我預估自己大概下個月就會忘記這次是怎麼寫出來的,所以這個懶人包對我來說也粉重要)
放了一個線上的範例網頁讓大家試敲,你可以輸入電子類股的股票代號、中文/英文股票名稱,網頁會提供提示,選取後會在左邊填入股票代號、右邊填入中文名稱。如下圖剖析,其實它是用<ul><li>去建構清單,而我特別框出selectValue,extra,會呼應到稍後提到findValue()函數中取值的邏輯。
程式的簡單說明如下。
前端HTML中主要透過$("...").autocomplete()的方式在Textbox掛上自動完成功能: 
(註: 原版的jquery.autocomplete.js有中文相容的問題,所以我用的是對岸網友修改過的版本,我自己則加了一個noCache參數。文件上說cacheLength設為1就不會做Cache,但我觀察結果似乎不然,也許是版本不同所致。當資料筆數很多時,Cache在Script Side的做法就會變得不合適,因此我加了一個參數鋸箭解決Cache問題。)
排版顯示純文字
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>jQuery自動輸入完成懶人包</title>
    <script src="jquery-1.3.2.js" type="text/javascript"></script>
    <script src="jquery.autocomplete.js" type="text/javascript"></script>
    <link href="jquery.autocomplete.css" rel="stylesheet" type="text/css" />
    <style type="text/css">
    input 
    {
        width: 60px;
        margin-left: 5px;
    }
    </style>
    <script type="text/javascript">
        $(function() {
//選項說明: http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions
            $("#txtSymbol").autocomplete("ACDataSrc.aspx",
            {
                delay: 10,
                width: 120,
                minChars: 1, //至少輸入幾個字元才開始給提示?
                matchSubset: false,
                matchContains: false,
                cacheLength: 0, 
                noCache: true, //黑暗版自訂參數,每次都重新連後端查詢(適用總資料筆數很多時)
                onItemSelect: findValue,
                onFindValue: findValue,
                formatItem: function(row) {
                    return "<div style='height:12px'><div style='float:left'>" + row[0] +
                            "</div><div style='float:right;padding-right:5px;'>" +
                            row[1] + "/" + row[2] + "</div></div>";
                },
                autoFill: false,
                mustMatch: true //是否允許輸入提示清單上沒有的值?
            });
            function findValue(li) {
                if (li == null) return alert("No match!");
                $("#txtSymbol").val(li.extra[0]);
                $("#txtCName").val(li.extra[1]);
            }
        });
    </script>
</head>
<body>
<input type="text" id="txtSymbol" />
<input type="text" id="txtCName" readonly="readonly" style="background-color: #cccccc;" />
</body>
</html>
另外,我寫了一隻ACDataSrc.aspx負責餵資料: (為力求單純,股票資料是用字串列出股票清單,實務上應該會以資料庫查詢取代之)
排版顯示純文字
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.IO;
 
public partial class jQueryAutoComp_ACDataSrc : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //使用者目前輸入的文字預設以q傳入
        string q = Request["q"] ?? "";
        if (q.Length > 0)
        {
            DataTable t = getStockData();
            DataView dv = new DataView(t);
            //利用LIKE做查詢
            dv.RowFilter = "Key LIKE '" + q.Replace("'", "''") + "%'";
            dv.Sort = "Key, Symbol";
            List<string> lst = new List<string>();
            lst.Add("");
            foreach (DataRowView drv in dv)
            {
                DataRow r = drv.Row;
                //組裝出前端要用的欄位
                lst.Add(string.Format("{0}|{1}|{2}", r["key"], r["symbol"], r["cname"]));
                if (lst.Count >= 10) break;
            }
            //每筆資料間以換行分隔
            Response.Write(string.Join("\n", lst.ToArray()));
        }
    }
 
    private DataTable getStockData()
    {
        #region 股票代號
        string rawData = @"
1435    中福    C.F.C.Y.CORP.
1437    勤益    GTM
...省略...
8249    菱光    CSI  
9912    偉聯    AIC";
        #endregion
 
        //如果資料量未多到誇張,將DataTable Cached住
        string CACHE_KEY = "StkTable";
        if (Cache[CACHE_KEY] == null)
        {
            DataTable t = new DataTable();
            t.Columns.Add("Key", typeof(string));
            t.Columns.Add("Symbol", typeof(string));
            t.Columns.Add("CName", typeof(string));
            t.Columns.Add("EName", typeof(string));
            //測試時由字串取得資料,實務上會去查DB
            StringReader sr = new StringReader(rawData);
            string line = null;
            while ((line = sr.ReadLine()) != null)
            {
                string[] p = line.Split('\t');
                if (p.Length != 3) continue;
                //分別以Symbol, CName, EName作Key
                //輸入中英文及代號都可以查
                t.Rows.Add(p[0], p[0], p[1], p[2]);
                t.Rows.Add(p[1], p[0], p[1], p[2]);
                t.Rows.Add(p[2], p[0], p[1], p[2]);
            }
            //放入Cache,保存兩小時
            Cache.Add(CACHE_KEY, t, null, DateTime.Now.AddHours(2),
                System.Web.Caching.Cache.NoSlidingExpiration,
                System.Web.Caching.CacheItemPriority.Normal, null);
        }
        return Cache[CACHE_KEY] as DataTable;
    }
}
實作細節請大家看程式碼自行揣摩,應沒什麼深奧的學問在裡面。如果有什麼對程式架構或寫法方面的指教,歡迎提出來大家切磋。
想抓整包回去玩的朋友,請按這裡下載。

2015年9月8日 星期二

入門與 PostBack觀念的練習

ASP.NET初學者 入門的觀念課

ASP.NET初學者要入門,有些觀念可能以前沒見過。

第一,以前寫過 ASP / PHP / JSP的朋友

在 ASP.NET裡面,會面對 Page的生命週期,例如:Page_Load事件與 IsPostBack(是否第一次執行?)。

為什麼按下 Button按鈕以後,Page_Load事件會重複被執行一次呢?

第二,最近在各論壇都有看見這個FAQ --

我想按下按鈕,每按一次,畫面上的數字就會累加(不斷地加一)

這是「狀態管理」,網頁程式的特色。以前寫過 Windows或是一般程式的人,會很陌生。



如果畫面上,不使用 Web控制項(如 TextBox或 Label)來存放 點擊(累加)的次數。

只允許您用 Response.Write()來呈現,那該怎麼作????



摘要:淺談 PostBack 機制

先在設計畫面拉出二個控制項:TextBox 與 Button
此時在網頁檢視原始碼,form 被轉譯為如下:

action 代表在 form 標籤內發出 submit 時所要導向的網頁,也就是自已本身網頁。


<form name="form1" method="post" action="Demo.aspx" id="form1">


此時button被轉譯如下:

就變成了 submit 功能的按鈕了。


<input type="submit" name="Button1" value="Button" id="Button1" />


所以當畫面上有 Button 控制項,點擊後會回貼本身網頁這稱為 PostBack 機制。


當 TextBox 啟用 AutoPostBack 後就具備 PostBack 功能

此時 HTML 原始碼會多出一段 javascript ,TextBox 會被轉譯如下


<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />

<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}
//]]>
</script>

<input name="TextBox1" type="text" onchange="javascript:setTimeout('__doPostBack(\'TextBox1\',\'\')', 0)" onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;" id="TextBox1" />



這時候有一個 onchange 的 JavaScript 事件,代表當 TextBox 的內容經過修改後就會引發。

引發事件時會去呼叫一個 JavaScript 函式 : __doPostBack(\'TextBox1\',\'\')

帶入有二個參數,第一個為引發此事件的表單之名稱,第二個為事件資訊


你會發現這個函式位於上方的 JavaScript 區塊,裡面做的事如下:

1. 取得網頁中的 form1 物件

2. 判斷是否 submit

3. 將傳進來的二個參數寫進最上方的二個隱藏欄位(hidden)

4. 發送 submit



此時就會發生 PostBack ,而此時在 ASP.NET 如何知道是由誰引發的事件呢?

就是靠之前所寫入的隱藏欄位(hidden)

ASP.NET 會使用 Request.Form["__EVENTTARGET"] 來知道事件是由誰所引發的


摘要:淺談 PostBack 機制(二)

並非任何控制項都具備 PostBack 功能

控制項必須實作 IPostBackEventHandler 介面才會具有 PostBack 功能

那該如何使沒有 PostBack 的控制項具有 PostBack 功能

例如 Label 控制項就不具備此功能,
使用 GetPostBackEventReference 方法


    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Label1.Attributes["onclick"] = 
                Page.ClientScript.GetPostBackEventReference(Label1, string.Empty);

            if (Request.Form["__EVENTTARGET"] == Label1.ID)
                Label1_OnClick();
        }

        private void Label1_OnClick()
        {
            Label1.Text = DateTime.Now.ToString();
        }
    }



有二個參數,第一個:將 PostBack 功能指定給控制項,第二個:指定事件參數

這時網頁原始碼如下

<span id="Label1" onclick="__doPostBack('Label1','')">Label</span>

所以也就是呼叫 JavaScript 的 __doPostBack 函式。


題外話:所以以此類推,要呼叫自訂的 JavaScript 函式也可以

Label1.Attributes["onclick"] = "myFunction()";

2015年8月28日 星期五

MySQL 的欄位名稱可以用空白字元

MySQL 的欄位名稱可以用空白字元

今天被一個 Bug 困住好久,可能是之前自己的手誤造成!!

原因就是...MySQL 的欄位名稱竟然可以使用空白字元,而且還可以放在第一個字元,我真是學藝不精!!

經測試,連資料表的名稱也可以使用空白字元:

CREATE TABLE ` test table` (
` field 1` TINYINT NOT NULL ,
PRIMARY KEY ( ` field 1` )
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_unicode_ci;
上面看得出我在資料表與欄位的名稱都用了空白字元嗎? 而且還是放在第一個字元!
我想,如果想要陷害別人絕對可以用 MySQL 這一招。
至於處理空白欄位名稱的問題,你可以:
  1. 將錯誤的欄位名稱改掉。
  2. 存取資料表與欄位時,在前後加上 ` `,例如: ` test table`.` field 1`。