2016年1月29日 星期五

IEnumerable?IQueryable?

昨天跟顧問討論著公司開發平台從下sql到寫Linq的各種心得
不禁有感而發
當初剛接觸Entity Framework和Linq時(公司則是用Telerik公司的DataAccess)
因為對IEnumerable和IQueryable的認知不清吃了不少效能上的悶虧
這兩樣東西的差別在於
IEnumerable :已執行Linq條件而產生的SQL,並將資料儲存在記憶體裡
IQueryable : 是一個指令的集合體,並沒有真的執行向資料庫取資料的動作

這一篇的重點應該就只有這兩句了
那到底該用哪個?這問題真是很難回答
只能看運用時機自己應變

舉個小例子

寫法一
var data = (from m in context.Members
                            join tm in context.TeamMembers
                            on m.MemberID equals tm.MemberID
                            join t in context.Teams
                            on tm.TeamID equals t.TeamID
                            where m.MemberID == memberid
                            select new { m, tm, t}).ToList();
foreach (var team in teamList)
{
      var m = data.tm.Where(c => c.tm.TeamID==team).First();

      //Do Something
}



寫法二
var data = (from m in context.Members
                            join tm in context.TeamMembers
                            on m.MemberID equals tm.MemberID
                            join t in context.Teams
                            on tm.TeamID equals t.TeamID
                            where m.MemberID == memberid
                            select new { m, tm, t});
foreach (var team in teamList)
{
      var m = data.tm.Where(c => c.tm.TeamID==team).First();

      //Do Something
}
大家來找碴
兩段程式的差別只在於寫法一多了一個ToList()
但背後實際執行起來卻大有差異

寫法一是在loop外面執行一次取資料
loop裡面的where都是針對記憶體裡的List在做處理

寫法二則是loop裡面每一個where才去跟資料庫要一次資料

還是如同前面所說的
哪一個比較好真的要看情況
真的不知道哪個好,那就兩種都跑跑看比較performance吧!!

2015年12月22日 星期二

關於OpenQuery帶參數的兩三事

前言
最近由於公司oracle主機更新
DBA盯長運算的sql盯得特別緊
於是幾個sql server上的古蹟就被挖出來了
也發現了幾個有趣的現象跟大家分享一下

最開始收到DBA傳來的sql是長這樣的
(雖然我覺得table schema算不上機密,但還是改編了一下)

select * from tableA where FLAG<>'Y' and NAME<> 'xxx'

這個tableA裡面包含了日期欄位
每天都有資料進帳,也就是說他是一個不算小的龐大table
看到這句sql不由得驚呼「怎麼會這樣子寫SQL!?」

追查了一下sql server上面的寫法後發現
在sql server上是這麼寫的

insert temptable
  SELECT * FROM OPENQUERY(oracle_con, 'select * from tableA') AS a
  WHERE FLAG <> 'Y' AND NAME <> 'xxx' AND ID IS NOT NULL
  and SDATE>@sdate


這樣的一句指令
在我本來的認知中他去了oracle應該就只有select * from tableA這段而已
有趣的他卻帶了兩個where條件過去,這有點顛覆了我對openquery的認知
不過更有趣的是,最後一個條件是帶參數過去不不意外
但為什麼ID IS NOT NULL這條件也過不去呢?

恩,雖然有趣但這神秘的規則就待以後再慢慢研究了
今天主要想提的是openquery的寫法
正確來說openquery該執行的應該是

select * from tableA' WHERE FLAG <> 'Y' AND NAME <> 'xxx' AND ID IS NOT NULL
  and SDATE>@sdate 


這樣的完整句子才能避免不必要的資源浪費並縮短執行時間
我們來改造一下這段
首先是openquery帶參數的部分
第一段
先來組sql字串吧

DECLARE @TSQL nvarchar(4000)
SELECT  @TSQL = 'SELECT * FROM OPENQUERY(oracle_con, ''select * from tableA WHERE FLAG <> ''''Y'''' AND NAME <> ''''xxx'''' AND ID IS NOT NULL
  and SDATE>'''''+@sdate+'''''
'') AS a '

接著執行

EXEC sp_executesql @TSQL
就有資料了~~~
咦?不對,我們是要insert到temptable裡我要怎麼把executesql裡面的東西拿出來呢!?
傻瓜,包進去就對了

DECLARE @TSQL nvarchar(4000)
SELECT  @TSQL = ' insert temptable
SELECT * FROM OPENQUERY(oracle_con, ''select * from tableA WHERE FLAG <> ''''Y'''' AND NAME <> ''''xxx'''' AND ID IS NOT NULL
  and SDATE>'+@sdate+'
'') AS a'
EXEC sp_executesql @TSQL

稍微說一下因為在nvarchar裡,一個'要變兩個'
然後openquery裡面的因為是字串裡的字串,所以兩個'要再變成四個'
總之就是很多個點
這裡隱藏了一個亮點
「為什麼你的SDATE沒有點!!!不對他是oracle的話你就算點了也沒用」
恩......不瞞您說.....SDATE這欄位.....是個NUMBER.......
如果真的要帶日期的參數.....Convert這麼好用自己轉一轉再用字串去帶吧!!



2015年4月21日 星期二

[C#]老師,外部執行檔不理我

這篇文章的出現是有歷史共業在的
隨著時光飛逝科技進步
一間公司經營久了系統多了人也老了
便開始出現了所謂的"舊系統"跟"新系統"
舊系統只要還堪用也舉不出什麼致命的缺陷
在人力資源不足的狀況下往往就會讓他繼續活下去
而新系統開發時在資源再利用的思維模式作祟下
就會有引用舊系統功能的需求出現
"寫成一個service大家用阿"
傻瓜,連dll都拿不到了還指望service
最後各退一步就是呼叫你的執行檔給你參數你output資料給我

賀,前言說了這麼多開始進入正題
協調好資料交換的格式之後,得到了一個精美的exe執行檔
裡面做什麼事你完全不知道,他會給你什麼結果你也無法預期
會跳error已經是很好的狀況了
最糟的是他什麼都不給你就一直hang住

同理心,執行檔會hang住肯定是出現了什麼他也沒預料到的特殊狀況才這樣
天助自助者,我們自己給的timeout設定不就得了
一般來說C#呼叫執行檔取output你會這麼寫
            string ret = string.Empty;
            using (Process proc = new Process())
            {
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.CreateNoWindow = true;
                proc.StartInfo.RedirectStandardOutput = true;
                //塞入你的參數
                proc.StartInfo.Arguments = parameter;
                //你的執行檔
                proc.StartInfo.FileName = exeFile;
                proc.Start();
                ret = proc.StandardOutput.ReadToEnd();
                proc.WaitForExit();
            }
在各種無法預期的不可抗力下
你很有機會在ReadToEnd()那邊等上一輩子

設定timeout的寫法

            string ret = string.Empty;
            using (Process proc = new Process())
            {
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.CreateNoWindow = true;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.Arguments = parameter;
                proc.StartInfo.FileName = exeFile;

                StringBuilder output = new StringBuilder();

                using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
                {
                    proc.OutputDataReceived += (sender, e) =>
                    {
                        if (e.Data == null)
                        {
                            outputWaitHandle.Set();
                        }
                        else
                        {
                            output.AppendLine(e.Data);
                        }
                    };

                    proc.Start();
                    proc.BeginOutputReadLine();






                    //人生苦短不該花費在等待上,設定一個timeout的秒數吧
                    int waitTimeSecond = 20;

                    if (proc.WaitForExit(waitTimeSecond * 1000) &&
                        outputWaitHandle.WaitOne(waitTimeSecond * 1000)
                    {
                        ret = output.ToString();
                    }
                    // Timed out.
                    else
                    {
                        ret = "老師,外部執行檔不理我";
                    }
                }
            }




2013年8月28日 星期三

[ios]縮你的鍵盤

作為一個xcode的新手,筆者今天卡在一個畫面相當久
诶........不好意思,請問我該怎麼關掉這個鍵盤

起初我一直認為一定是我沒用過iphone跟他不熟不會操作
抑或是模擬器少了什麼鈕可以按
尋尋覓覓了許久....我放棄,還是請出google大神吧
不料在01上看到了這麼一篇

WTF!!?只是要關閉一個鍵盤居然得使用密技!?
同時在搜尋結果中看到了一個"Cxxxa輕鬆收起你的iPhone鍵盤"的app
連這種app都出現了,我更加確定的相信這個結論........
iPhone的鍵盤沒有讓你收起它的地方!!!

好吧,介面上辦不到,我從程式裡動手總行吧
接下來才是今天的重點
怎麼收鍵盤?
其實只是很簡單的一個method
    [self.tbTime resignFirstResponder];
tbTime是我的TextField
我有兩個TextField可能造成這個鬼擋牆的鍵盤
所以就是
    [self.tbLength resignFirstResponder];
    [self.tbTime resignFirstResponder];
不用特別去判斷TextField是否有focus,沒有focus呼叫這method也不會怎樣
接著就是觸發的時機點了
搜尋鈕的Click事件是一定要的,事件裡複製貼上就好了
接著額外衍生的問題來了
大部分查到的做法都是利用背景Click的時候來縮鍵盤
但我整個畫面都被MapView佔領了,背景就那麼一點點
且MapView沒有Click事件可以用耶
這時就要靠UITapGestureRecognizer這東西了
先直接奉上程式
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc]
                                   initWithTarget:self action:@selector(handleGesture:)];
    tgr.numberOfTapsRequired = 1;
    [self.myMap addGestureRecognizer:tgr];
    
    [self refreshLocationData];
}
這段是說,我在load的時候宣告了一個UITapGestureRecognizer
handleGesture做的事情如下
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
    [self.tbLength resignFirstResponder];
    [self.tbTime resignFirstResponder];
}
就是剛剛隱藏鍵盤的那兩行啦
接著指定這個tgr觸發的tap次數,我們希望點一下就縮鍵盤,所以是1
最後再將tgr新增到MapView中就大功告成啦!!

2013年8月20日 星期二

[ios]iOS6 程式設計實戰 - 136個快速上手的開發技巧

非常不愛看書的我
今天要來推薦一本工具書
碁峯的iOS6 程式設計實戰 - 136個快速上手的開發技巧
在開始介紹這本書之前,首先必須告知
這本書並不是一本程式語言的教學
所以他不會從Objective-C的基礎開始教起
僅有在一開始稍微介紹了這語言的特色與需要注意的部分而已
而Objective-C的語法教學在網路上有相當多的資源可以學習
所以我們也不需要買一本從頭到尾都在說語法的書

筆者選書第一件事一定是看目錄
如果目錄的章節看起來就是
第一章 背景介紹
第二章 開發介面介紹
第三章 變數型態
第四章 迴圈與判斷式
這種書我會毫不考慮地放回書架上略過
因為每本書都長這樣,網路上找來的資源也都長這樣
那你該如何說服我買你呢?
回到這本書的目錄
如圖中所示
每一章介紹的是一個技術功能
書中一開始便告訴你,不建議你把這本書從第一頁開始看到最後一頁
因為有些東西你並不需要
但你可以把每個章節的標題掃過一次
當你開始開發一個app時,你會碰到各式各樣的功能需要實現
你需要開始尋找Objective-C以及iOS能幫你做什麼
這時再回到目錄來,翻到你需要的章節,取得你想學的東西

章節內的介紹我只能說是符合我的喜好
未必是適合大家
每個段落個一開始先簡單介紹過本段落的主要功能
並告知應先讀過哪些章節
接著的重頭戲,他將整個實作過程以條列式方式一步一步寫出
並明確說明每個動作應該寫在哪個地方
條列式最棒了!
你可以無腦的完全照著打過一次
再依照個人需求來做調整、修改
筆者學生時從來沒當過程式作業的源頭
因此拿別人的程式來改這件事完全就是我的style

當然,也不是多厚的一本書
當中一定會有一些變化的應用沒有完全說明
但有了基本用法以後,相信你一定可以在google上面以關鍵字找到更多變化
有明確的步驟以及修改即可完成的半成品
開發app這檔事,請容許我報告,完全沒有問題!!

書本資訊
書名:iOS6程式設計實戰  136個快速上手的開發技巧
出版社:碁峯
作者:朱克剛
定價:480 (p購物可以找到79折)

2013年6月28日 星期五

[android]特別企劃~垃圾車在哪裡,絕對難不倒你

某個月黑風高的夜裡
筆者因為錯過了家門口的垃圾車
拿著手機連上環保局,看著沒有支援手機板的網站
痛苦的查著只有地址的垃圾車班表
不熟悉的路名還要貼到google map去找
提著一包垃圾騎著歐兜賣
在不斷的等待與錯過中就這樣在路上折騰了好久
最後才將垃圾交給一台偶遇的垃圾車結束這一天的冒險

有鑑於此
我發願要把這一切簡單化
筆者跟java不熟,也從來沒寫過任何app
但我相信google威能,於是就開始訂定了我需要的api和資料

更新,1跟2的資料
以高雄市來說,現在可以直接下載到垃圾車的座標資訊
https://waste.ksepb.gov.tw/
使用上方便太多了
/*
1. 垃圾車地點、時間資料
萬事起頭難,我決定第一步先不碰java
先用自己熟悉的C#來準備好需要的資料
雖然是用熟悉的技術
但這個是讓我搞了最久的部分
http://abandoned.kbj.com.tw/waste/search <--垃圾車資料網站
自己摸著摸著挖出了用QueryString來列所有資料的方式
http://abandoned.kbj.com.tw/waste/search/0/0/0/0/0 這是我最後找到的
最後一個參數是決定開始顯示的index
而每次固定顯示15筆,於是只能for loop了
這個處理資料的過程就不贅述了
基本上就是用HttpWebRequest抓畫面中的表格分析資料塞進sqlite
而各地環保局的網站各自成一格
若要將這功能發揚光大至全台灣恐怕花最多時間的都是在蒐集資料這工作了

2. 補足定位點資料
現在有了地址我打算連同座標也整理好才放進app
app做的事情只有讀出座標判斷距離顯示在地圖上
這邊使用的是google map的api
var url = String.Format("http://maps.googleapis.com/maps/api/geocode/xml?sensor=false&address={0}", address);
address帶的當然就是地址了
當中xml是我選比較習慣的格式,也可以用json的格式取得回傳結果
另外sensor這個參數一定要帶,不然查不出來
取出的xml分析的動作用XmlDocument就可以搞定了
格式及用法可參考 https://developers.google.com/maps/documentation/geocoding/?hl=zh-tw
另外google map這個api有每天2500次Query的限制
每秒內的Query次數也有限制
因此我為了補足這些資料洗了好幾次ip
*/


3. 資料內嵌進app
準備好的資料要送進app內
這邊我參考的是這篇
http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/
作法完全照抄網頁上的方式

4. Google Maps Android API
基本上這個app要達到的目的就是在地圖上顯示自己的座標和附近的垃圾車座標
而我相信google map會是一個好選擇
設定及使用方式參考了這個
http://cheng-min-i-taiwan.blogspot.tw/2013/04/google-maps-android-api-v2-android.html
溫馨小提醒
在Google API Console要打開的是Google Maps Android API v2不是Google Maps API喔
筆者由於眼殘在這邊卡了一下

5. 取得定位點
GPS定位這個只是個小技巧
網路上的資源多如牛毛
但大部分都是做個Class來implements LocationListener
我是懶人所以我則是參考了這篇
http://zhuyonghui116.blog.hexun.com.tw/43948361_d.html
另外發現在requestLocationUpdates後他就隨著周期卯起來一直定了
然而我只有在開啟app和按下更新時想去跑一次
所以還有參考了這篇
http://hp.dewen.org/?p=576

結合以上的東西,這個app大致上算完成了
剩下的就是換個美美的圖再把exception修一修
畢竟是個不熟悉的開發工具跟語言
整個過程其實也花了不少時間
但這也證明了一件事
你可以不會寫程式也可以不會用IDE
但你一定要會用google search

2012年9月4日 星期二

[VS]visual studio 2010 之 消失的scrollbar

雖然說vs2012都出了
不過小弟我最近才剛開始從vs2008轉到vs2010
題外話,雖然我的NB只是兩萬塊的便宜貨又沒有ssd
但i5開vs2010慢成這樣也著實令我驚訝

安裝完也是用了很久的telerik web元件之後
遇到了神秘的狀況,我的toolbox的scroll bar消失了
用滾輪也滾不動,下面還卡了好幾個東西選不到
試了一下子之後發現了這個神祕的現象
不囉嗦直接看影片
一直展開項目開到一定程度之後scroll bar就會消失
同時可以發現的是scroll bar的拉bar小到一個程度以後就不再縮小
看起來是個故意設定的行為
理由為何就要問問微軟囉

2012年8月9日 星期四

[windows]我的圖哩??

今天這一篇,實際上跟寫程式一點關係也沒有
只能算是一個windows設定的線上問題處理經驗分享

故事是這樣的
昨天早上接獲通報,我做的winform功能在某台電腦上都顯示不出圖片
"某台",一聽就覺得是電腦狀況造成的個案不太想理
不過進去以後發現了有趣的現象
不是所有圖都不見,也不是只有我的功能圖會不見
交叉比對了大家的功能內容之後發現一件事
消失的只有BackgroundImage,就是背景圖
這下子我開始對這問題產生興趣了
這是一個winform程式又不是web會有不同瀏覽器造成的靈異現象
究竟是怎樣的一個狀況可以造成這種情形呢
讓我們繼續看下去

一、修個案必經路程--重開機
很遺憾,這個嘗試完全沒有效果

二、換個帳號登入
同一台電腦其他User登入跑同一份程式居然是正常的
這種現象要嘛是權限要嘛是設定
把測試帳號開到power user的權限試試
遺憾,無效,往下一步發展

三、既然是圖片,應該是顯示設定吧(?)
不才小弟我就這樣在這個設定畫面卡了一個早上
翻過來又翻過去,把裡面每個設定值都玩了一次
依然無解

四、一切都只是巧合
窮途末路想到頭都痛起來的我
痴痴地對著控制台畫面發呆
這時很不巧的讓我看到了這一行字
Windows用了這麼多年,第一次覺得他的註解這麼重要
這時心想"是阿,會不會有盲人不想看到背景圖只想聽聲音?"
但我馬上覺得自己這個想法五告北七
即使如此,我還是點進去了
罷了,死馬當活馬醫試試看囉
進去協助工具選項->畫面
恩,沒什麼選擇性阿
游標就不用看了,高對比是啥?
應該只是讓他更明顯吧,不然勾掉看看囉
就在這時,一切的謎團全都解開了,圖全都回來了
但是,另一個謎團又出現了
究竟為什麼一個高對比會讓我的圖全都神隱呢???

這時我回到自己的電腦想再複製一次這個現象看看
我是Win7,win7的高對比藏在輕鬆存取中心裡面
點畫面中那個小項他很北七的開去控制台->外觀及個人化->個人化
要先點輕鬆存取中心->設定高對比
跟WinXP不同的,裡面有很多細項選擇
這邊就不一一提了
但其中有一個選項能夠完美的解釋這一切糾結的因果
我想不用做太多解釋了,各位客官直接看圖吧


2012年5月21日 星期一

[C#]幫我換行謝謝

今天不才小弟接到一個需求,是要將一串樂樂長的字串印到小小標籤上
每行22個字元,gotta be twenty two,twenty two lucky
不喇賽,換行而已嘛,很簡單,大家都會換行,不就是\n\r這麼簡單
不過後續來了,因為有人反應,單純用22這個幸運數字來抓index切字串的話
英文單字會被腰斬屍首分散兩地,所以必須判斷相連的英文字太長就去下一行
這功能用過word的人都知道這功能
就是這樣的感覺

遺憾的是標籤列印的程式不在我這邊
所以不能挑一個會自動換行的列印程式來印,我必須幫他換行
這時問題來了
我要去判斷連接的字和空白嗎?
字串裡包含中英文數字和特殊符號,分析這個會不會太"搞剛"?
因此我心頭一轉,C#的RichTextBox不是也會自動換行
如果宣告一個RichTextBox設定寬度讓他幫我換行不好嗎?
身為一個極力幫自己偷空閒時間的programmer,我想這是一個最快速的解法
以此方向為基準產生了第一次嘗試
        private void button1_Click(object sender, EventArgs e)
        {
            //要換行的字串
            string sourcetext = "Who knows what tomorrow brings In a world where few hearts survive All I know is the way I feel When it's real I keep it alive The road is long There are mountains in the way But we climb a  step every day Love lift us up where we belong Where the eagles cry On a mountain high Love lift us up where we belong Far from the world we know Up where the clear winds blow Some hang on to used to be Live their lives looking behind When all we have is here and now All our lives Out there to find The road is long There are mountains in the way But we climb a step every day Love lift us up where we belong Where the eagles cry On a mountain high Love lift us up where we belong Far from the world we know Up where the clear winds blow";
            //宣告一個RichTextBox來用
            RichTextBox rtbWrap = new RichTextBox();
            //設定自動換行
            rtbWrap.WordWrap = true;
            //設定寬度
            rtbWrap.Width = 140;
            //字串給rtb
            rtbWrap.Text = sourcetext;
            string resultstr = "";
            foreach (string line in rtbWrap.Lines)
            {
                resultstr += line + "\n";
            }
          MessageBox.Show(resultstr);
         }

看起來一切都很順利,但是出來的結果....
說好的換行呢?
遺憾的,rtbWrap.Lines裡面只有一個字串,也就是原本的樣子
這下可好,我要怎麼抓他的每一行呢?
不用怕,微軟有他貼心的一面RichTextBox中有這麼一個method
GetFirstCharIndexFromLine
這東西會根據你給他的行號回傳那一行第一個字的index
當沒有那一行的時候他則會回傳-1
有了這東西就一切好辦了啦
這是成果
        private void button1_Click(object sender, EventArgs e)
        {
            //要換行的字串
            string sourcetext = "Who knows what tomorrow brings In a world where few hearts survive All I know is the way I feel When it's real I keep it alive The road is long There are mountains in the way But we climb a  step every day Love lift us up where we belong Where the eagles cry On a mountain high Love lift us up where we belong Far from the world we know Up where the clear winds blow Some hang on to used to be Live their lives looking behind When all we have is here and now All our lives Out there to find The road is long There are mountains in the way But we climb a step every day Love lift us up where we belong Where the eagles cry On a mountain high Love lift us up where we belong Far from the world we know Up where the clear winds blow";

            //宣告一個RichTextBox來用
            RichTextBox rtbWrap = new RichTextBox();
            //設定自動換行
            rtbWrap.WordWrap = true;
            //設定寬度
            rtbWrap.Width = 140;
            //字串給rtb
            rtbWrap.Text = sourcetext;

            string resultstr = "";
            int i = 0;
            while (rtbWrap.GetFirstCharIndexFromLine(i) != -1)
            {
                int thislineindex = rtbWrap.GetFirstCharIndexFromLine(i);
                int nextlineindex = rtbWrap.GetFirstCharIndexFromLine(i + 1);
                if (nextlineindex == -1)
                {
                    resultstr += sourcetext.Substring(thislineindex) + "\n";
                }
                else
                {
                    resultstr += sourcetext.Substring(thislineindex, nextlineindex - thislineindex) + "\n";
                }
                i++;
            }

            MessageBox.Show(resultstr);
        }

就是這樣
換行的標準取決於RichTextBox的寬度,寫活一點的話可以隨著紙張大小去做調整
各位請自行運用