2012年9月6日 星期四

C# - 如何知道泛型的型態?

因為某種需要,所以開了一個 table ,
在裡面的開了一個 varchar 的欄位,
雖然是 varchar 的型態,
但是實際上會存入各種資料型態 bool, int, decimal, string...
原本以前的笨蛋寫法會是像是下面這種直接回傳字串的宣告,


public string GetOptionValue(string optionName)


然後呼叫的人再去把字串轉型成自己要的型態。

不過我是一個很懶的人,
所以我想說如果每次要用的人都自己還要轉型,
我實在是很懶得每次都還要寫一樣的 code ,
雖然可能只是多了一行,但是我就是不爽寫!
那可以怎麼做呢?
這個時候我想到了也許可以利用泛型來處理,
泛型的宣告如下,


public T GetOptionValue<T>(string optionName)


2012年8月28日 星期二

C# - Threading using Task


之前在【C# Threading Finial】裡面已經把目前我所知道 Threading 跟 UI 配合的方法 demo 了一次,
上次同事告訴我說在 .net 4.0 (VS2010) 裡面提供了一個新的方式 Task 來寫 Threading ,
所以我就把上次【C# Threading Finial】裡面的程式拿來改成用 Task 的方式,
測試了一下,的確是簡單了許多,
不過規矩還是一樣,跟 UI 的互動還是得交給主執行緒,
所以還是得用 Invoke 的方式來呼叫,
其實我覺得比較麻煩的還是跟 UI 的互動,其他的倒是還好,
接下來就來看看上次那個程式怎麼修改成 Task 的方式吧!
首先原來的 btnThreadingQuery_Click 就變成下面這樣啦!
改成用 Task.Factory.StartNew 的方式來呼叫 QueryMain()


private void btnThreadingQuery_Click(object sender, EventArgs e)
{
    //ThreadPool.QueueUserWorkItem(new WaitCallback(QueryMain));
    Task.Factory.StartNew(() =>
    {
        QueryMain();
    });
}


然後主程式主要的修改如下:

2012年8月16日 星期四

C# - 如何複製其他物件的事件


之前遇到了要強制驅動物件的事件【C# 如何強制驅動物件的事件?】,
也遇到了要移除物件的事件【C# - 如何移除未知名稱的事件 - LookupEdit.KeyDown 】,
正所謂無三不成禮,這次要做的是複製其他物件的事件!
說複製其實不太對,正確地來說應該是把其他物件的事件掛(delegate)上來。

例如我們可能在 A 物件寫了一個 KeyDown 的程式,


private void ObjectA_KeyDown(object sender, KeyEventArgs e)


在 designer 裡面會是寫成這樣,


this. ObjectA.KeyDown += new
System.Windows.Forms.KeyEventHandler(this. ObjectA_KeyDown);


當我們的 B 物件也要用同一個事件的時候,我們只要手動用 += 就可以掛上這個事件了!
但是問題又來了,如果我們不知道他事件的名稱,那我們要怎掛?
其實前面一大段程式都跟之前移除事件的程式一樣,
只是後面要怎麼掛上去呢?說穿了很簡單!
(其實是會寫之後才覺得簡單 XD)
程式如下,跟之前程式最大的差別就是最後面兩行,

2012年8月10日 星期五

Oracle - Function return list

莫名其妙的開始要看 Oracle 的東西,
第一個遇到的難題就是要幫別人看 PL/SQL ,
但是我根本不會寫呀!
自從二技那堂 Oracle 的學分之後我就再也沒有摸過了,
連書都送別人了!可見我多麼有自信不會再摸到 Oracle 呀!
可是上天是愛捉弄人的!

anyway, 現在要弄得就是寫一個STORE PROCEDURE\FUNCTION 回傳一個 TABLE,
本來覺得這根本是天殺的簡單呀!
沒想到小朋友寫出來的卻是回傳一個 CURSOR !!!這是甚麼東!
結果才發現這邊的人居然沒有寫過這樣的東西,
還質疑說 Oracle 可以這樣子嗎?這問倒我了,我哪知道呀~~~
所以我就東查西查的,還問了阿輝仔!
原來 Oracle 真的不能直接回傳 SELECT * FROM xxx_table 這樣!
不過可以用間接的方式,往下看吧!

首先要先宣告你要回傳的 List Array 的 OBJECT:

CREATE TYPE O_GROUP AS OBJECT
(GROUP_ID VARCHAR2(8),
GROUP_NAME VARCHAR2(30));

然後接下來在將這個 OBJECT 轉成 TABLE:


GREATE TYPE GROUP_LIST AS TABLE OF O_GROUP;


然後就可以開始寫 Function 啦!如下:

2012年8月6日 星期一

更換程式圖示之後還是顯示舊的圖示的鬼問題!

一般我們辛苦寫完一個程式之後都會給程式一個小圖示,
以便拉出捷徑的時候可以有一個漂亮的圖示顯示在桌面上,
不過上星期我卻遇到一個軌問題,
就是呢........把舊的 icon 改掉之後拉出捷徑卻還是顯示舊的!
原本個人天真的以為這個可能是 windows 有 cache 住的關係,
所以使出萬法規宗的重開機大法應該就可以了吧?沒想到不管用!
在嘗試的同時,隔壁的同事說他之前有遇過,要去 registry 刪除,
打開 regedit 之後果然裡面有很多那隻程式名稱的機碼,
刪刪刪之後重開機.............還是不行!
所以就開始估狗囉!
只不過這個關鍵字還真的不知道該下甚麼,
不過後來有找到一篇
【[Windows7]修復桌面與開始功能表中錯亂或是消失的捷徑圖示 - Ouch@點部落- 點部落】
果然 windows 有 cache 住圖示,
依照該篇網誌的指示直接用 cmd 的模式切換到下面的目錄

CD C:\Users\Frankie\AppData\Local

然後 cache 的檔案叫 IconCache.db ,
但是因為他是隱藏的,所以要先解掉隱藏這件事情。

attrib -h IconCache.db

然後這樣才可以刪除它。

del IconCache.db

然後重開機,然後還是不行.........................
馬的,試了好幾次都還是不行,怪!難道是因為這支程式自己寫的關係嗎?
試了好多次,最後不管三七二十一,
我把 registry 裡面的程式名稱都刪除,也把 IconCache.db 刪除,準備跟它同歸於盡!
重開機之後居然OK了!
所以結論就是兩個都是刪除,為了這個浪費了我好多時間喔!
心想,如果客戶說要換圖示那不過搞死我了!
可能要在更新程式的時候順便做這件事情,然後叫客戶重開機?!?!
我覺得我應該會被打死,應該有重建 IconCache.db 的指令吧?
等我快被打死之前再來孤狗好了!

2012年7月14日 星期六

SQL - 中文長度的問題、LEN、DATALENGTH

因為轉檔的關係,有時候需要把名稱的欄位簡短後塞到簡稱的欄位去,
不過常常都會出現的狀況就是....



即便我已經用了 SUBSTRING 也是一樣!
但是我用 LEN 看一下 SUBSTRING 之後的長度明明就沒有超過呀?
後來上網查了一下有關 LEN 中文長度的搜尋,
然後找到了一個新的函數 DATALENGTH ,
大致上 LEN 的算法是中文算長度1、英文也算長度1,
然後 DATALENGTH 的算法是中文長度2、英文長度1,
中間的差異大概就是一個算字元數,一個算BYTE數吧?
所以當我寫出下面 SQL 的時候,算出來的數字是完全不一樣的!

LEN(name_ch) AS "LEN", DATALENGTH(name_ch) AS "DATALENGTH"



於是乎我就想說那 SUBSTRING 有沒有類似的函數可以用呢?
很遺憾的是我沒有找到,也許 SQL SERVER 2020 會支援吧?
所以當我寫下面那樣的話還是會出現二進位截斷的問題,

SUBSTRING(name_ch, 1, 10)

最後我只好改用 CONVERT 的方式來寫,如下:

CONVERT(VARCHAR(10), name_ch)

如果大家知道 SUBSTRING 有類似 DATALENGTH 的函式可以用的話麻煩通知我一下嘿!

2012年7月9日 星期一

如何在 Linq 裡面使用類似 SQL 的 IN 的語法?

需求是這樣的,使用者會輸入一個日期區間、並挑選業務員,
我在查詢的畫面會【列出所有的主檔資料】讓使用者勾選要列印哪一個主檔的資料,
但是當使用者按下一個【filter】的按鈕之後,
我會過濾出那個業務員有業績的資料,
然後取代剛剛那個【列出所有的主檔資料】的畫面讓使用者勾選。
以前的作法會大概會是過濾的時候順便 JOIN 主檔的資料,
然後列在主檔的畫面讓使用者勾選。

不過我在作的時候突然想到,我前端都已經有全部的主檔資料了,
為什麼還要再 JOIN 一次呢?
為什麼還要再增加那台五年前很強的主機的負擔呢?

所以我就想說有沒有辦法可以在前端作 JOIN 或是用 IN 的方式呢?
摸索了一下, Linq 沒有 IN 可以用,不過找到了另一個方式可以代替,
在我還沒有找到其他方式之前就先這麼辦吧!

測試的畫面大概長這樣!最左邊列出所有的產品,中間列出指定業務員有交易過的產品。


當業務員選擇 Frankie 的時候,則只列出 Frankie 有交易過的產品資料。
所以就不是全部的產品了!


選擇 Mork 的時候,顯示出有交易過的產品又更少了!


程式碼如下:
  1. string sales = leSales.EditValue.ToString();
  2.  
  3. DataTable filterList = DBUtility.GetDataFromDB("deal",
  4.     string.Format("SELECT DISTINCT product FROM deal WHERE sales = '{0}'", sales));
  5.  
  6. var q = filterList.AsEnumerable()
  7.     .Select(f => f.FieldByName("product").AsString);
  8. var filterResult = allProducts.AsEnumerable()
  9.     .Where(r => q.Contains(r.FieldByName("code").AsString));
  10.  
  11. DataTable filterProducts = null;
  12. if (filterResult.Count() > 0) filterProducts = filterResult.CopyToDataTable();
  13. grdFilter.DataSource = filterProducts;

這邊有用 DevExpress 的元件,所以有些物件的屬性不懂就跳過吧!

1.先取得業務員代號。

3.取得業務員曾交易過的產品代號。

6.將交易過的產品存到 q 裡面。

8.這個時候就可以用 q.Contains 當 SQL IN 來用了!

13.最後將過濾過的資料集塞回去 Grid 就可以了!