2017年11月20日 星期一

MS Enterprise Library 6.0 (五) - 再談AOP, 如何實作?

這系列的上一篇居然是2013年…沒說錯,是2013,四年前!!!

MS Enterprise Library 6.0(一) - Unity Application Block
MS Enterprise Library 6.0 (二) - Logging Application Block
MS Enterprise Library 6.0 (三) - Exception Handling Application Block
MS Enterprise Library 6.0 (四) - Policy Injection Application Block

說真的,如果不是因為開了課,我根本早忘記我曾經寫過上面這幾篇文章。

上週六,是『團隊開發 與 架構設計 實務 』這個課程的第二天上課,在課程中我們談到了一個框架設計上我常用的技巧,也就是如何透過Attribute來實作AOP(嚴格說起來是廣義的AOP)。如果你不確定明白AOP(Aspect-Oriented Programming)的基本概念,可以參考四年前的這篇。(天啊,四年前…)

課程中,我做了一個範例(具體內容後面說),和某位學員討論的時候,突然看到學員的螢幕上出現了一篇看似有點眼熟的文章,語氣跟我好像…咦? 作者怎麼是我?是了,就是剛才說的四年前寫的那篇這篇文章中介紹了PIAB(policy injection application block),這是當年我在Tech Days介紹MS Enterprise Library時,特別喜愛的一個application block,它很精采的把Business Logic和Infrastructure Logic做一個非常有效的切割,達成cross-cutting concerns的獨立性。

這一系列的文章或許是因為點擊率不很高,也或者是當時有別的事情再忙…我寫著寫著居然就忘了…也因此這個系列就沒了下文。時過境遷,這次上課的時候,我也不再介紹PIAB,因為這個PIAB Library的nuget套件很不爭氣的始終停留在2013年都沒更新,因此我們只能從底層架構說明透過Attribute實現AOP概念的作法。

不過這樣也好,學員可以從根本理解實現AOP可能的幾個方式 。然而,美中不足的是這樣去實現AOP的Method呼叫時,就得要有另一個Loader/Lancher來做attribute行為上的判斷。

『這樣呼叫Method時似乎有些不直覺?有沒有更好的做法?』學員問。

『其實你可以自己仿Unity Container做一個Proxy的機制…』因為在課堂上我們還有一併介紹到Unity,因此我這麼回答。

然而因為時間的關係,我也就無法在課堂上直接做一個Demo,想想頗為遺憾,因此課程結束後的這幾天,我抽了點時間把這個心裡想要實現的小套件,仿造PIAB的方式來製作一下。

需求與命題

首先來整理一下這個命題,我們知道AOP想把Business Logic和Infrastructure Logic做一個切割,達成cross-cutting concerns的獨立性,具體情境是什麼呢?看底下這一段code:

上面這段程式碼的第10行使用了Calculate()這個Method來計算BMI,但這段Code有些小問題,例如,傳入身高體重是0時候,可能發生Exception。

你可能會想,這不要緊,我們在Method中加入try…catch,類似底下這樣(當然我知道可以加上傳入參數的檢查,但我想講的點是try…catch啦,所以我們先這樣幹):

這樣做沒問題,但如此一來,我們就讓計算BMI這段code,嚴重的與處理exception中的發mail或發Line訊息的功能相依!

此外,倘若我們想針對該Method被呼叫幾次,做一個Log,你可能也會在Calculate()這個Method中加入讀寫檔案存取log或db的code,這下不得了,簡單的BMI計算(business logic)開始與檔案系統、DB、發mail…etc(Infrastructure  logic)相依,萬一你程式碼中不只一個地方需要進行Log,而是有好幾個method都需要log,那就更精采了…

而AOP希望實現的,就是讓這些Business Logic(BMI計算)和Infrastructure Logic(Log, 錯誤處理, …etc.)之間可以不要彼此過分相依…

如何實現呢?可以透過Attribute。

想像一下,倘若我們可以這樣做:

倘若我們可以透過對Method掛上我們寫好的Attribute,就賦予Calculate()這個Method特殊的功能,例如一旦發生exception,就自動發Line訊息通知Admin,如果該Method被呼叫,就自動在Log file中記錄…。倘若這一切只需要掛上Attribute就能實現,我們就毋須破壞原本計算BMI的Calculate()這個method中的程式碼了…

但,真的可以這樣嗎?
當然可以…

請在專案中透過nuget加入一個套件 isRock.Framework,完成後,請先引用using isRock.Framework.AOP:

接著設計一個Logging Attribute(請注意繼承自PolicyInjectionAttributeBase):

我們在上面這個自行設計的Attribute當中,透過SaveLog這個方法來儲存Log。並且override了AfterInvoke()來呼叫SaveLog()。

完成後再將原本計算 BMI的Class修改如下(請注意只改了1, 13行):

最後,再調整一下Main中的程式碼(只改了第5行)。

神奇的事情發生了。

你會發現,我們的主程式,上面的main()中其實只改了小小一行(第五行),就讓我們自己寫的Logging attribute作用到Calculate()的身上了。由於Logging attribute中的第6行override了AfterInvoke()這個method,該Method會自動在Calculate()方法被呼叫後自動被執行,其中的SaveLog()程式碼是開檔寫Log…

如此一來,每當Calculate()方法被呼叫,log.txt檔案就會被開啟並被加入一列,變成:

這樣就實現了我們一開始的需求,當某一個Method被掛上Attribute之後,就能夠增加原本沒有的功能。且將Business Logic和Infrastructure Logic做一個切割,達成cross-cutting concerns的獨立性。

我們回憶一下我們做了什麼?

  1. 首先我們引入了Nuget套件 isRock.Framework
  2. Using了isRock.Framework.AOP
  3. 接著設計了一個自己的Logging Attribute(繼承自PolicyInjectionAttributeBase)
  4. 在上述Logging Attribute當中,override了AfterInvoke()方法,該方法會在目標Method(被掛上attribute的Method)被叫用之後自動被呼叫
  5. 在上述AfterInvoke()方法中呼叫SaveLog()開檔並加上『目標Method被呼叫』的Log
  6. 為了讓attribute生效,我們修改原始的BMIProcessor類別,在Calculate()方法上掛上LoggingAttribute:
    [Logging(LogFileName ="log.txt")]
  7. 並且讓BMIProcessor類別繼承自PolicyInjectionComponentBase
  8. 調整主程式為
    BMIProcessor BMI = PolicyInjection.Create<BMIProcessor>();

就這樣,我們透過這個框架,可以讓開發人員輕鬆的設計自己的Attribute,並且將attribute掛在Method上,實現Business Logic和Infrastructure Logic分離的AOP情境。

骨子裡,他還是使用PIAB的作法,透過reflection與Proxy的手法,完成以attribute實現類似AOP的功能:

如果你深入看,會發現我們的PolicyInjectionAttributeBase已經實做了BeforeInvoke、AfterInvoke、OnException三種Method,可讓開發人員輕易的實現自己想實現的Attribute。

嚴格說起來,我們用PIAB這樣的手法雖然可以實現部份AOP的目標,但還夠不全面,這也是PIAB不稱自己為AOP框架的原因。但是,即便只是能實現到目前這樣,也已經很符合我們在日常開發工作當中使用了。

這篇先寫到這邊,改天我們來談談如何實現OnException的Attribute。

完整程式碼可以參考:
https://github.com/isdaviddong/isRock.Framework.AOP-Examples

參考資料:
Introduction to the Policy Injection Application Block

-----------------
相關教育訓練: http://www.studyhost.tw/NewCourses/Architecture
若這篇文章對您有所幫助,請點選這裡加入FaceBook專頁按讚並追蹤,也歡迎您幫我們分享出去,謝謝您的支持。

2017年11月2日 星期四

好工具其實就藏在民間 - 圖檔去背

有一個很讚的網站,叫做 https://clippingmagic.com/ 這個神奇的網站,可以幫助你用簡簡單單的一筆一畫完成圖檔的去背。

你只需要像是上面這樣,用綠筆把圖檔要保留的部分畫起來,再用紅筆劃掉不想保留的部分,就能得到一個去背後的圖檔,剛出來的時候炫爆了。

最近這個網站不炫了,原因是,它…開始收費了。

以前去背靠這網站現在該怎麼辦呢?

雖然其實也不貴,但還是潛意識反射動作的在拿出信用卡前,找找有沒有其他的替代方案…上網查了一下,哇靠,原來我們偉大的PowerPoint內建這個功能!!

你只需要點選要去背的圖檔,然後選擇格式->背景移除,就可以了,它除了會幾乎自動化的幫助完成去背,也一樣可以讓你用綠紅兩色的筆,選擇自己要保留或是移除的部分。

完成之後,把去背成功的圖檔另存新檔就可以了:

PowerPoint…還真是不能小看你的啊

2017年11月1日 星期三

the DevOps journey - VSTS預設Build當中說好的程式碼測試涵蓋率沒出現?

有一天上課,講到單元測試,學員聽到VSTS預設的範本就可以在CI/Scheduled Build當中自動運行單元測試,並且產生出測試涵蓋率的報表,大家都很興奮(雖然我不知道為何大家都那麼興奮…)

然後Lab時就開始下手開幹了,Build裡面運行unit test沒問題,出來的結果如下:

No Build Code Coverage Data Avaliable????

什麼鬼?說好的測試碼涵蓋率呢?

莫急莫慌莫害怕…

請回到Build Step中的Test Assemblies, 預設狀況下Code Coverage Enabled是沒有勾的…

勾上之後,老闆要的測試碼涵蓋率就出現囉…

(A:上面那個範例只寫了一個測試,啊是有什麼好涵蓋的啦…)
(B:沒關係,讓PM看到圖就大家開心了…)

-----------------------------
本系列文章索引位於 http://studyhost.blogspot.tw/2017/02/the-devops-journey-index.html
相關教育訓練: http://www.studyhost.tw/NewCourses/ALM
若這篇文章對您有所幫助,請點選這裡加入FaceBook專頁按讚並追蹤,也歡迎您幫我們分享出去,謝謝您的支持。

2017年10月30日 星期一

the DevOps journey - 在VSTS的CI流程中透過FTP實現自動佈署

一般來說,我們在進行VSTS教育訓練時,都透過Azure Web Site做CI後的自動佈署,達成一個基本的CD流程。

要在VSTS的Build中『順便』將Build好的成果佈署到某一個Azure Web Site上,基本跟喝水一樣簡單,因為VSTS的Build definition範本中,預設就有這個功能:

你會發現,建立出來的Build Process當中,有預設的Azure App Service Deploy這個task:

把該填的資料填一下,就可以Build完順便自動將Build好的結果佈署到某個azure網站,這樣很讚,自動到你不知道怎麼回事就完成了。

但,如果你一開始建立的範本忘了選Deploy,也可以手動加上這個Task:

很容易,對不對? 但學員卻常常有一個問題。

公司高層考慮了很久之後,終於想通了願意用VSTS或TFS,但問題是要佈署的網站不是Azure(因為這部分依舊沒想通,覺得在雲上太不安全或太貴了,堅持要用自己家裡或其他hosting的IIS),那怎麼辦呢?

沒問題,把Azure App Service Deploy換成FTP就好啦,你可以在 Visual Studio Marketplace中找到一個免費的FTP Uploader套件:

位於 https://marketplace.visualstudio.com/items?itemName=januskamphansen.ftpupload-task ,點選上圖中的Install安裝到你的VSTS或TFS站台之後,你就會在UtilityTasks中看到一個新的FTP Uploader套件:
和VSTS裡面的FTP Upload task不同的是,它相對的比較直覺,並且會自動幫你過濾掉不該上傳到網站上的一些檔案(例如.cs之類的source code),我們可以用它來取代原本的Azure App Service Deploy,將一些該設定的參數設定一下,就可以完成自動佈署囉:

留意上面的參數,以我們熟悉的Azure Web Site為例,帳號密碼欄位你不會有任何懸念,而FTP Address指向root即可,Remote Path如果對照Azure Web sites則是/site/wwwroot,而source path可以用下拉的方式選擇,拿asp.net來說,指定Web Site Root,一般而言選擇web.config所在的目錄就可以了。

你會發現,FTP Uploader會自動幫你過濾掉source code(.cs)之類的檔案,如果你想手動過濾掉一些你不想upload上去的檔案,下面這邊也可以做些設定:

就這樣,非常簡單好用。

如此一來,你的CD要佈署到任何位置的網站都可以囉。

------------------------------
本系列文章索引位於 http://studyhost.blogspot.tw/2017/02/the-devops-journey-index.html
相關教育訓練: http://www.studyhost.tw/NewCourses/ALM
若這篇文章對您有所幫助,請點選這裡加入FaceBook專頁按讚並追蹤,也歡迎您幫我們分享出去,謝謝您的支持。

2017年9月28日 星期四

關於LineBot(13) – 使用Date Time Picker讓用戶輸入日期資訊

這個月(還是上個月?忘了),Line Bot增加了DateTimePicker功能,可以在Template Message當中加入了DateTimePicker Action,當用戶點選之後,即可用手機預設的UI選擇時間日期,並讓開發人員以postback參數的方式取得用戶選擇的日期。

這功能非常有價值,因為自然語言對談的過程中,如果要讓用戶輸入日期,用戶難免自行發揮,格式千奇百怪,對於開發人員來說會是一個困擾。有了這個功能,當Line bot需要用戶輸入日期時,不僅格式統一,且使用者輸入時也可以少按一些,更為方便。

使用此功能不難,您只需要建立一個Template Message,並指定Action的類型為DateTimePicker即可:

上面程式碼當中(A)的位置,就是建立所需要的DateTimePickerAction,而(B)所指定的參數mode,則是跳出時間日期選擇器的類型,可以是date/time/datetime其中之一。

範例中其他的程式碼都跟一般的TemplateMessage建立沒兩樣,完成後發送出訊息,就會看到類似底下這樣的畫面:

一旦用戶選定值並按下『傳送』之後,我們就可以透過WebAPI(WebHook)取得的postback參數來收到這個值,程式碼如下:

請留意上圖中(A)的部分,您可以在收取到的訊息的postback物件中,取得Params底下的datetime/date/time屬性,這個屬性會依照先前你在Action中指定的類型,被填入相對應的值。

例如,剛才我們建立Action時,第一個action是mode=time這個類型,因此,當用戶點選該action,Line會跳出時間選擇框讓用戶選擇時間,用戶選定後,Line bot就會產生一個postback,因此我們的後端WebAPI被觸發,同時間ReceivedMessage.events[0].postback.Params.time會被賦予用戶選擇的值:

整個操作的順序大致如上圖所示。

透過這個功能,當Line bot需要用戶輸入日期時,就更加的方便了,可以預期的,未來不管是GPS座標位置、或是更複雜的選擇項目,都可以透過類似的機制來完成,非常讓人期待。

別忘了 LineBotSDK請升級到 0.6.0-beta

完整程式碼請參考:
https://github.com/isdaviddong/LineBot_DateTimePicker

------------------
相關課程: http://www.studyhost.tw/NewCourses/LineBot
LineBotSDK : https://www.nuget.org/packages/LineBotSDK
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2017年8月29日 星期二

關於LineBot(12) – 在連續對話中加入ButtonsTemplate訊息

如果你實際開發過Chat bot,就會發現NLP(自然語言分析)本身就是一個很大的問題,即便現在有LUIS可以幫助我們做語意分析,但實務上分析結果要100%符合用戶(或開發人員)的心意不是易事,倘若可行,在商務應用時,Chat bot與用戶之間的一問一答對談,想要提高可用率,讓用戶以選項的方式回答還是比自然語言對談來的好很多。

舉例來說,在上一篇我們介紹的CIC類別當中,如果我們的chat bot要問用戶要請哪一種假別,與其讓用戶自己回答,不如讓用戶用選的乾脆一些。

原本對談的設計是:

但這風險很高,因為我們開放用戶自己用輸入的方式回答。

如果用戶偏偏要回答:
我今天想請事假
    或
請婚假
    或
什麼根本沒聽過的假…

對Chat bot來說都要做自然語言分析,就算分析的再正確,在開發上難度也提升不少,但如果用底下這樣的ButtonsTemplated Message詢問用戶,用戶只能在底下的選項中擇一回答,那就容易多了:

上一篇說過,我們要透過繼承自ConversationEntity的LeaveReuqest類別來處理這對話邏輯,而這一版我們加上了支援ButtonsTemplate Message,你只需要使用ButtonsTemplateQuestion這個Attribute即可(注意第3行):

我們新建立的這個LeaveRequestV2,跟上一篇的LeaveRequest很像,但第一個詢問假別的問題,改用了ButtonsTemplateQuestion Attribute(注意,你必須把LineBotSDK升級至 0.5.7-beta),程式碼就只需要做這一個小小的調整即可。

由於加上了ButtonsTemplate Message,chat bot WebApi Controller主程式碼也小幅修改一下:


只需要配合ButtonsTemplate Message在59-59行針對CIC的Process result,取得ResponseButtonsTemplateCandidat(64行)進行Reply即可。

其他的程式碼跟上一版幾乎完全一樣沒變。

而24-29行只是加了一小段判斷,規定用戶只能選擇事假、病假、公假其中之一…純粹只是寫著玩測試一下。

執行這段對話之後,chat bot可以蒐集用戶的回答,結果如下:

想要在ConversationEntity中使用ButtonsTemplate也沒有問題囉 Smile

完整程式碼請參考:
https://github.com/isdaviddong/LinebotConversationExample/blob/master/LinebotConversationExample/Controllers/CICTest2Controller.cs

------------------
相關課程: http://www.studyhost.tw/NewCourses/LineBot
LineBotSDK : https://www.nuget.org/packages/LineBotSDK
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2017年8月24日 星期四

關於LineBot(11) – Chat bot如何處理連續對話

開發對談機器人,一直有一個很關鍵的議題,就是如何處理連續性的對話。

這個問題,上課時學員問過、研討會時聽眾問過、公司同事問過、客戶問過、學校老師問過、差不多連路人甲(我是說對開發chat-bot不熟但剛好聽到chat-bot開發的路人甲)都很關心…

舉例來說

舉一個典型的例子,如果我們要開發一個聊天機器人來處理請假,那這對話大約是底下這樣:

請注意用戶開始跟chat-bot說:『我要請假』之後,chat bot進入了一個連續對談模式,在這個連續對談模式中,chat bot以一問一答的方式,跟用戶蒐集請假資訊,依序詢問用戶請假假別、代理人、時間…等資訊。

最後,蒐集完所有資訊之後,進行請假動作(上面的例子是以JSON方式把蒐集到的資訊顯示出來,沒有真的請假,但意思到了)。

需求就是這樣。這有什麼難的?  其實,還真的不很容易。

首先,如果你不想把程式碼寫死,想用同一套框架或邏輯來處理同性質的會話(conversation),這本身就有不低的難度。其次,每一個一問一答的對談之間,由於是同一個WebHook來處理的,因此要記錄狀態(state),諸如:這次問的是什麼、用戶回答的答案是對應到哪一個問句、目前的交易是屬於哪一個用戶…,這是第二層難度。

接著,用戶回答的型別如果不符合? (例如問’請假時間’,結果用戶回了一句’事假’) 該怎麼辦?  還有,就算用戶回答的資料型別正確,但邏輯上不合理(例如代理人用戶寫Tom,但公司就明明沒有Tom這個人),又該怎麼處理?

想起來,如果chat-bot要寫這種會話對談,將會是一段很恐怖的程式。

但,我實在被問過太多太多太多次這個問題了,因此,索性,找了一個心情很差的周末,我嘗試寫一個能夠處理這樣對談的框架。

怎麼用?

假設,你要處理上面這樣的對談,你可以建立一個類似底下這樣繼承自ConversationEntity的類別:

先別指摘我上面這個範例中的類別怎麼會用中文當屬性名稱,這是故意的。這是為了讓讀者更好理解,正式環境你當然可以換成英文。

但,這類別真的很好懂,是吧?

這類別表示了『請假(LeaveRequest)』這個對話將會有五個問句,第一句是問用戶『請問您要請的假別是?』,用戶回答的結果會被放入『假別』這個屬性中。第二句是是問用戶『請問您的代理人是誰?』,用戶回答的結果會被放入『代理人』這個屬性中。

其他的應該不需要解釋了吧?

注意,請假日期這個屬性的型別是DateTime,因此用戶如果回答了不合理的型別,我們的chat bot會說:

直到用戶輸入符合格式的資料為止。蒐集到所有需要的資料之後,chat bot就會結束這一輪對談:

蒐集完畢的資料,會以物件的方式回傳給我們的WebHook,我們可以進而拿來請假(或像是上面這樣的例子只是將其顯示出來)

如何開發

具體程式碼要怎麼開發呢? 別急,我們一步一步來。

首先你先定義一個前面介紹過的類別,我們稱為LeaveRequest,這個繼承自ConversationEntity的類別是用來存放請假資訊,同時也用來描述對話的順序和問句,比較特別的地方是Question與Order這兩個attribute:

前面說過,這兩個attribute用來描述這個『請假會話』的問句與詢問順序。

接著,我們撰寫一個非常典型的WebHook:

你會發現,其實和原本前面幾篇文當中的WebHook沒啥不同,唯一的差別就是12行的CIC物件,在定義這個物件的程式碼中,我們引用到了先前定義的LeaveRequest這個ConversationEntity。

CIC類別我稱為Information Collector,就是幫你使喚Line bot依照你定義的ConversationEntity來向用戶透過連續問句蒐集資訊的類別。

它會依照你定義的ConversationEntity(本例中為LeaveRequest),讓chat-bot一問一答取得資訊,至於前面提到過的什麼狀態管理(State)、型別判斷…等有的沒的,他都幫你搞定了。

16,18行是原本的WebHook,20行是定義一個取得CIC返回結果的物件。

而21行則是看到關鍵字『我要請假』,就透過24行命令CIC進入『請假會話狀態』。30行則是將每一個WebHook收到的event都丟給CIC物件,不管目前是否在『請假會話狀態』之中。CIC物件會自動判斷,把結果透過result回傳分析結果。如果目前不在對話狀態中,CIC不處理並回傳pass,如果所有資料蒐集完畢,CIC回傳done,如果判斷用戶輸入的型別部正確,CIC回傳InputDataFitError,如果用戶中斷,CIC回傳break…

因此,34-64行則是依照CIC可能的回傳結果逐一處理,例如,如果用戶輸入的資料型態不符合(要DateTime,但用戶輸入String),會進入56行的InputDataFitError,這也是前面這個例子結果呈現出底下這樣的原因:

用戶可以依序輸入資料,直到每一個資料都符合規定為止(CIC會回傳done這個result),當然,用戶也可以輸入一些關鍵字來中斷會話:

觀察上面的程式碼,你會發現其實這是CIC觸發了52行的break result的結果。

如何?透過定義一個繼承自ConversationEntity的LeaveRequest類別,搭配一個CIC物件,就可以實現這樣的chat bot對談蒐集資料的效果?這框架還過得去吧?

資料邏輯判斷

你可能會覺得… ok, looks fine. 到目前為止,我只需要定義一個ConversationEntity,它就自動幫我完成資料蒐集,看起來很炫,但實務上還有很多問題啊…

第一個就是,如果用戶輸入的資料,在型別上符合,但在邏輯上不符合怎麼辦?例如,我們想請用戶輸入『代理人』,但用戶可以隨便亂輸入,或,假設用戶輸入Tom,但公司明明沒有Tom這個人,那怎麼辦?

好問題,請看解法如下:

注意上面程式碼6-19行,我們對CIC加入了一個OnMessageTypeCheck事件,在該事件中處理收到的用戶輸入e.ReceievedMessage來判斷即可。

例如上面這幾行程式碼,是判斷當用戶輸入的是代理人屬性,而輸入的值又不是eric的時候,就直接跟用戶說,本公司只有eric啦…

這樣,CIC就可以處裡所有的邏輯問題囉。

範例位於這裡,如果你想用自己的專案搶先玩玩的話,LineBotSDK請記得升級至 0.5.6

待解問題

有人問:如果不想讓用戶隨意回答,希望能夠用Templated Message的形式讓用戶只能用選的,那怎麼辦?
David: 下一版再加入此功能。

有人問:如果用戶堅持不按順序回答那怎麼辦??
David: 下下一版再加入此功能。

有人問:那如果OOXX該怎麼XYZ呢???
David: 下下下一版再加入此功能?

David: 還有其他問題嗎? 沒有? 很好。 Smile

------------------
相關課程: http://www.studyhost.tw/NewCourses/LineBot
LineBotSDK : https://www.nuget.org/packages/LineBotSDK 
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。 

2017年8月5日 星期六

別再只問我要答案,這次沒有!

這幾年做教育訓練、顧問服務、和系統導入,發現一件有趣的事情。

有幾次碰到一些客戶(有商業公司、非營利組織、學校…都有),在開會或課程之後私下跟我說,希望我們在提供建議的時候,可以『直接』告訴我們的team member,這件事情該怎麼做,而不用花太多時間告訴我們這些事情的原則,或是背後的邏輯或原理。因為team member只需要快速地得到答案,加上事情很緊迫,他們也沒有時間去學,說多了徒增困擾(這句話有個潛台詞,建議讀者細想,很有趣…),顧問(講師)直接告知做法之後,我們就可以比較快進入下一個問題(議題)。

可能有人看了上面這段話不明白意思,我舉其中一個比較具體的例子。

有一次碰到一個軟體開發的資安問題,我花了幾個小時解釋為何程式碼這樣寫不行,為何這有可能帶來風險,從這樣的程式碼我們可以得知外包的開發人員應該有哪些問題…,幾個小時下來,我發現,客戶唯一關心的事情只有一個: 那現在這行程式要怎麼改? 直接告訴我就好。

至於其他的,大夥兒絲毫不在意。

這類的例子最近層出不窮。由於樣本數雖然多但我沒徹底研究,因此目前我無從取得確切的證據,證明背後的原因是什麼?

但推敲之後,能猜個大概。

可能是因為這年頭大家都很忙,碰到問題,只想快速知道答案。而網路的便利性讓許多人變成只願意(也似乎可以)迅速的取得結果,久而久之,慢慢地不願意也沒耐心花太多時間深入理解問題背後的成因。

也就是說,大家並不真的在乎(到願意付上代價、例如時間)來面對問題、或學習解決問題的能力。許多人變得只想知道,當下眼前這問題怎麼解決。

『告訴我解法就好,其餘的我不想多知道。』似乎已經是網路速食時代的寫照。

這是最近幾年碰到的,以往,大概十年前,這情況比較不常發生。但不用我說大家應該也知道,長久下來這(對企業、或團隊)會導致什麼結果。

再加上,網路時代很多流傳文章,標題長這樣:『OOO的原因原來是XXX』、『解決XYZ的N種方法』、『想要OOO就得XXX』這類的文章都很紅。標題看似一針見血,但內容往往很武斷。大部分的文章(或課程)中提出的見解或解決方案,其實都隱含著一個假設前提,就是你的環境和他(作者)相同,然而你也知道,真實世界中哪有環境都相同的? 礙於篇幅(也礙於其實讀者也沒興趣知道),因此大多數文章的作者,也不太會詳細敘述案情的成因與環境的背景,導致部分憨直的讀者以為,我照著做就會有一樣的結果,其實不然。

一個問題的解決方案,依照環境、人員狀況、時空情境…常常會有著不同的解法。我們在客戶端導入開發方法、框架、或Scrum,不同團隊、不同時空碰到的同一個問題,提出的建議往往是南轅北轍,甚至看似彼此互斥。

曾經有助理跟著我,一周內到兩家不同的公司,客戶問出的問題幾乎完全一樣,但我對兩家客戶提供的建議卻整個大不相同,助理一開始很遲疑,久了之後就慢慢理解為何我們會這麼做。

但,外人乍看之下一定很難理解背後的原因。

不過你可能會質疑,那這樣一切豈不都是自由心證,沒有標準可循? 這樣怎麼知道講師或顧問說的是對是錯?

其實不然,你可以分辨,只要你知道為什麼? 顧問給一個建議,你需要問為什麼,講師給一個做法,你也得知道為什麼?

我常說:知道『為什麼(Why)』永遠比知道『怎麼做(How)』還重要!

要知道,我們專注的是『目標』,各種具體的解決方案都只是手段。目標即便完全一樣,但面對不同的情境,就可能會有不同的做法。但如果學員(team member、客戶)只在乎得到一個答案,卻不在意這答案為何出現? 怎麼來的? 背後的思維是什麼? 考慮的重點為何? 有哪些情境上的先決條件? 背後需要配合的技術環節有哪些? 會不會有副作用? … 那獲得的只是『這次的』解答,僅此而已。

要知道整個解決方案出現的思考脈絡和架構,才是真正有價值的重點,這也才是確保你日後能夠獨立面對問題的關鍵。

我明白,我真的不是不知道,對很多人來說,先把問題解決比較重要...

但這就好比我在台上講時間管理的課程,試圖告訴學員如何安排事情的優先順序,但突然間有位學員大聲喊:『 停、停、別講那麼多道理,直接告訴我下禮拜我該先做什麼就好了...』

這…有點怪,不是嗎?

碰到問題,直接要答案,不是不行,只是可惜…

2017年8月4日 星期五

關於LineBot(10) - 取得用戶上傳給Linebot的照片

在用戶與你所設計的Linebot對談的過程中,我們常常會需要接收用戶傳遞過來的資料,除了文字之外,最重要的就是圖片(或照片)了。

用戶可以從手機或PC版的Line上面,輕易地上傳圖片給我們的linebot,這圖片來源可以是直接抓取相機拍照的照片,或是從手機上的相簿、圖庫裡面抓取,當然也可以從第三方的資料來源(像是Dropbox)把圖片傳遞給Line。

因此,圖片會有各種不同的格式,但慶幸的是,Line會幫我們把圖片做初步的處理(例如對太大的圖進行縮小、調整格式…等),我們想要抓取處理好的結果,只需要簡單的使用API即可。

透過我們的SDK,可以輕易的使用底下這個API來取得用戶上傳的圖片,回傳的結果是bytes[] 陣列。

當然,從上面你不難發現,要取得圖片時,我們需要傳遞給API一個ContentID參數,這參數怎麼來呢?

透過這樣的方式,就可以順利的取得用戶上傳的圖片資料囉。

補充一:

別忘記一件事情,我們並不能假設用戶上傳的一定就是圖片(搞不好是文字?或是GPS座標位置啊…),因此,採用上面那兩段程式前,你必須先判斷,用戶這次傳遞給我們的訊息是否為圖片:

補充二:

抓到圖片Bytes後,要怎麼辦?
一般來說,我們會在WebHook裡面抓取圖片,而WebHook在.net多半是以WebAPI的方式撰寫,因此,在你抓取到圖片Bytes之後,會有幾個考慮。

  • 該圖片要不要讓其他系統使用?(例如azure影像辨識API,或是後端其他系統)
    如果要,你多半需要把Bytes儲存為實體的檔案,這部分可以透過  System.IO.File.WriteAllBytes(路徑, bytes);
    這樣的Method來完成,如果是圖檔,附檔名直接給png即可。
  • 該圖片要不要讓用戶下載?
    如果要讓用戶下載,則該圖檔不僅僅要儲存,還必須要有一個可公開的URL,這樣我們會建議你把圖檔存放到azure blob,或是在你的WebApi所屬網站上開一個folder,用上述WriteAllBytes指令存檔時,儲存到該公開folder中即可。當然,如果你要把圖檔放到資料庫中也行。

------------------
相關課程: http://www.studyhost.tw/NewCourses/LineBot
LineBotSDK : https://www.nuget.org/packages/LineBotSDK 
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。 

2017年8月2日 星期三

天下本無事


今天去剪髮,幫我剪髮的設計師和我認識了20多年(很神奇吧),從她年輕的時候年輕的我就讓她剪髮,到現在,20年,在同一家店。

也因為如此,我每回去剪髮的時候,都會回想起20年前的自己,今天我想的是,20年前的自己和現在的自己,在意的事情有哪些不同?

我問她,她覺得自己這20年有何改變? 她說:『沒有啊,我沒想過這個問題。』『我從來都不會想這些奇奇怪怪的事情…』

我微笑『這樣很好啊...日子單純多了。』我說。
她回答:『對啊,反正想了也不會改變什麼,那幹嘛想?』

『你知道嗎?』我想起來。『有一本暢銷書,花了很長的一段篇幅,在告訴人們,多花點時間關注自己能改變的事情,而不要花太多時間去憂慮自己無法改變的事情上。』

她聽了笑著說:『這也要寫一本書跟大家講? 既然自己不能改變,那花時間去想幹嘛呢? 愈想愈煩悶而已。而且,花在這樣的事情上愈多時間,不就愈沒時間去做自己能改變、能帶來影響的事情嗎?』

『是啊,不過...』我說:『有時候這就是人的本性,雖然理智上知道,該專注在自己能施力、能造成影響的事情上,但大多數人總是對那些自己無法改變的,像是政治制度、經濟、疾病、外在環境、輿論、他人的看法...這些事情上,花了太多的時間,卻總是忽略了、或放棄去經營自己能造成改變、能發揮影響力的地方。』

『好在我沒這個困擾,可以洗頭囉...』她說。

洗頭的時候我在想,這個道理我十年多年前從書上看到,但一直到現在,這十多年來,我真的專注在自己影響範圍的事情又有多少呢?

---------------------------------------

ref 1: 關切範圍 & 影響範圍
ref 2: The 7 Habits of Highly Effective People
ref 3: How to stop worrying & start living

2017年6月21日 星期三

關於LineBot(9) - 取得新加入的好友身分資訊

在知道了如何建立LineBot的WebHook之後,許多朋友開始有了另一串問題。其中我最常被問到的就是,如何在用戶加入你的bot為好友的時候,取的用戶的身分xk7。

這個問題中有兩個關鍵:

  1. 用戶加入你的bot時,你如何得知?
  2. 如何取得特定用戶的身分資訊?

這兩個動作都不難,都可以在我們的WebHook中取得。

首先,當有一個新的用戶加入你的Bot為好友時,我們可以從WebHook取得的JSON得知,透過我們的SDK,你可以很簡單的以底下的程式碼判斷:

if (ReceivedMessage.events.FirstOrDefault().type == "follow")

{

}

因為新加入好友或是被解除封鎖,都會收到type是follow的event,因此上面的程式碼很容易判斷出有新的好友來了…

而得知新好友(用戶)來了之後,我們可以透果底下這一行取得用戶資訊:
var userInfo = bot.GetUserInfo(ReceivedMessage.events.FirstOrDefault().source.userId);

得到userInfo物件之後,你可透過 displayName 輕鬆取得用戶暱稱,也因此,我們把用戶暱稱顯示出來:

bot.ReplyMessage(ReceivedMessage.events.FirstOrDefault().replyToken, $"哈,'{userInfo.displayName}' 你來了...歡迎");

得到的結果就是:

當用戶加入某個bot為好友,或是解除封鎖,都會收到follow,我們上面的程式碼抓到用戶名稱,顯示出來,結果就是上圖這樣。

很容易吧,試試看囉…

hope enjoy it…

此範例原始程式碼位於 : https://github.com/isdaviddong/LineBotExample_GetUserInfo/blob/master/LineBotExample_GetUserInfo/Controllers/LineBotController.cs

--------------------------------------------
相關課程: http://www.studyhost.tw/NewCourses/LineBot
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。 

2017年6月2日 星期五

關於asp.net framework的本質

很久沒有聊聊asp.net了,但畢竟asp.net跟我緣分不淺,或許該找個時間來談談。不過,今天我們試試看換一種方式,也讓大家自我挑戰一下,題目來囉...

  1. 我們說asp.net是一個web開發框架(framework),現在的asp.net framework提供了三種不同的方式(途徑、架構、風格)來建立網站,分別是哪三種?
  2. 這三種分別有哪些特性(或優缺點)呢?
  3. 這三種可以混用嗎?

來公布答案。

首先,千萬別執著對錯,因為我這題目本身就很爛(題意不清),其實我們應該把.net core和asp.net分開來看...這題談的不是asp.net core, 但題目沒說清楚,會讓人誤會。

因此,重點不是對錯,而是我們要藉著這題目來釐清整個asp.net的架構與觀念。

首先,題目與答案都在這邊:

ASP.NET is great for building standards-based websites with HTML5, CSS3, and JavaScript. ASP.NET supports three approaches for making web sites. ASP.NET Web Forms uses controls and an event-model for component-based development. ASP.NET MVC values separation of concerns and enables easier test-driven development. ASP.NET Web Pages prefers a single page model that mixes code and HTML markup. You can mix and match these techniques within one application depending on your needs - it's all One ASP.NET.

上面這段話的來源是:
https://www.asp.net/get-started/framework

所以,從這邊我們可以知道,先撇開.net core不談,asp.net framework (on windows),主要approaches Web Sites的方法有三,分別是 WebForms, MVC, 與 Web Pages...這三者位階相同...有趣的是…Web Pages是最常被忽略的。

而另一些常見的答案諸如 Web API,SignalR ...也是asp.net框架的一部分(但不能說它主要是用來『作網站』的,嚴格說起來它們的用途是通訊)。

接著,你繼續看上面那段英文的最後一段,這三種技術,在技術上『是可以』混用的
(David註:只要你夠熟)

而特色呢?分別是:

Web Forms : uses controls and an event-model for component-based development.
MVC : separation of concerns and enables easier test-driven development.
Web Pages : a single page model that mixes code and HTML markup.

上面Web Forms和MVC的特色說得很清楚,Web Pages可能有人會覺得有點語焉不詳,但你可以從底下這邊找到更多說明:
https://www.asp.net/get-started/websites

從上面你可以知道, Web Pages有點像是過去的ASP(Active Server Pages),提供開發人員一個最輕量級的方式來建立dynamic web content(動態網頁內容,就是從伺服器端動態抓取資訊,塞到html裡面往前端送的那種東西),和過去ASP不同的地方是,Web Pages它主要採用了Razor View。

用比較容易理解的方式來說明:
Web Pages : 最簡單,如果IT/MIS只是要臨時開發一兩個頁面,我會推薦選擇這個方式。
Web Forms : 事件驅動、主要模擬視窗物件導向開發的控制項、具有視覺化開發、快速開發(RAD)...的特性。
MVC : 主要價值就是關注點分離(SoC)、因此也最容易實現測試驅動開發。

我自己覺得MVC對於開發人員的素質要求相對比較高,如果單就一個功能點來說,透過這個方式寫出來的Code也肯定會最多。因為SoC,因此一個功能本質上應該拆成Model/View/Controller三塊來看(來思考),架構上相對複雜(因為比起單純的頁面多了Routing、Controller的概念),對於初學者進入障礙就高。

而Web Forms則是開發速度快,馬上能看到結果,容易上手,但也相對最不容易開發出職責分離的架構。

Web Pages則是最直覺容易開發,但基本上沒什麼架構可言,如果頁面一多,系統一寫大基本上難以維護,但如果只是幾個頁面,那當然就非常方便好用。

就體質上來說,Web Pages 不容易寫出有職責分離、有架構、分層負責的程式碼。但Web Forms和MVC都可以(當然前提是開發人員素養夠)。Web Forms要寫出具有架構職責分離的程式碼得靠毅力(因為容易被惡魔引誘),而MVC則是會強迫開發人員往這個方向走。

寫MVC的人如果沒能做到SoC 或 unit test/test-driven,那採用MVC的價值就大幅降低了,因為他的主要目的就是在這。而Web Forms則是為了模擬傳統的Windows 應用程式開發方式所生出來的框架,事件驅動的 event-model 和控制項是其特色,但代價就是為了去模擬狀態(State)而導致產生臃腫的ViewState。

MVC和Web Pages對於HTML Tag控制的自由度最高,而Web Forms則相對很低(特別是大量使用模擬桌面應用程式開發的Web Controls時)。

最後再順便談談 asp.net core,這個.net core,主要是為了讓系統能跨平台(例如run在Linux上),為了這個跨平台,所有與IIS和windows平台特性的相依性都必須拿掉,這意味著過去你熟悉的system.XXX之類的namespace開始無法使用,導致.net code基本其實就是整個打掉重練,跟原本的asp.net骨子裡差異很大。

但打掉重練也就沒有包袱,可以有新的開始,但當然也意味著你過去學的不一定都能延續。至於要不要使用.net core,或是跨平台時乾脆就換一種開發技術(例如node.js),這見仁見智,看你自己的選擇了。

所以簡單的幾個結論...

  1. 如果你想跨平台,可選用.net core(但我們今天上面那個題目,其實主要討論的不是這部分)。

  2. 如果你想寫較具有架構的網站或Web應用程式,你可以選擇 MVC 或 Web Forms,

  3. 如果只是想寫幾個頁面從伺服器端撈資料呈現給用戶,不是什麼大架構大系統,可以考慮 Web Pages。

MVC和Web Forms幾乎是不同的框架,使用目的基本上不同,也很難直接比較,但選用時其價值與核心精神(上面有討論)則必須掌握。否則若選了某一種開發方式卻沒有用到其特性,就很可惜了。

而真的有必要時,只要你夠熟悉,這三種寫法在技術上是可以混在一起用的。
(.net core則當然無法跟傳統的asp.net on windows混用)

---------------------------

以上,有沒有幫你更釐清asp.net的本質?
還是...你被搞得更暈了呢? :)

其實,不管你使用哪一種技術建立網站,只要能發揮其特性,避開缺點,凸顯其價值,並且在團隊中能夠對成員與老闆解釋的清楚,對企業來說,已經是很難得的人才了。

相關文章: http://studyhost.blogspot.tw/search/label/Framework
相關教育訓練: http://www.studyhost.tw/NewCourses/Architecture

2017年4月11日 星期二

the DevOps journey – 在Visual Studio版控比較多個檔案差異

這功能一點都不特別,但很重要,而且我記得我是在用了很久Visual Studio的TFVC版控之後,某一天偶然發現的。

我不知道是否每個人都跟我一樣,我自己在寫程式的時候,習慣在程式碼Check-in之前,對比一下這次修改了哪些東西? 確認無誤之後再Check-in ,會養成這習慣,是因為這小小的動作救了我很多次。

有不只一次,當我這麼做時,留意到了一些原本沒注意到的細節,諸如…在Method裡面忘了檢查參數內容是否正確? 沒有判斷空值或null是否合理? IO處理可能發生exception的地方沒有掛上try…catch…等等,太多了,只要養成review自己的code的習慣,你會發現,常常能購找到可以改善之處。

這個動作會迫使我在簽入前重新看一次剛才自己寫的code,有點自己review自己程式碼的感覺。所以,現在每當我程式寫到一個段落之後,都會習慣的在要比較的那隻程式碼檔案上,點選一下滑鼠右鍵,選擇Compare…比較一下和上一個版本的差異…

但這樣可能會漏掉了某些改過的檔案沒注意到,因此,最好的習慣是在Check-in的那一刻,在該檔案上點選Compare with …

因為版控機制幫你列出了所有修改過的檔案,如此一來,你就不會錯過哪一個檔案。

一般來說,過去在簽入程式碼到伺服器端前,我肯定會一個一個逐一比對,看看和伺服器端的檔案有何差別。(確認改了哪些東西)。

但有時候,Visual Studio會把附屬性質的檔案(例如.aspx.cs, .aspx.designer.cs,當作已修改,但其實它並沒有被改到,真正改到的只有.aspx而已。這時候,一個一個比對很煩人。(浪費時間)

不知道從哪一天開始,我突發奇想,難道Visual Studio不能一次比對多個檔案,先幫我確認哪些檔案有改過,讓我只review有改過的檔案嗎? 有了這個想法,當場嘗試看看能不能用滑鼠Mark多選幾個要簽入的檔案,發現,居然可以耶…

但,卻不能比較(Compare選單disable了)

不死心,又突然想到,那如果選擇資料夾呢?

咦? 可以耶,快來試試看。
你會發現,點選Compare後,會出現底畫面:

Visual Studio真的去幫我對比整理出了所有該資料夾下的檔案,僅列出有修改差異的部分,你還可以在該檔案上double-click,看到細部的差異比較:

這節省了我很多時間,我只需要把程式改完,在check-in前面,一次比對所有檔案的差異,不僅讓我安心check-in,對提升程式碼品質也有助益。

Visual Studio,地表最強開發工具,你當之無愧。
------------------------------
本系列文章索引位於 http://studyhost.blogspot.tw/2017/02/the-devops-journey-index.html
本網站不放廣告、完全免費。若這篇文章對您有所幫助,請點選這裡加入FaceBook專頁按讚並追蹤,也歡迎您幫我們分享出去,謝謝您的支持。

2017年3月23日 星期四

the DevOps journey (9) – 在TFS/VSTS中使用Git版控

接著,我們來看如何在TFS/VSTS中使用Git形式的版控。

同樣的,請先建立一個支援Git版控的Team Project,與TFVC版控的Team Project不同,建立好該專案之後首先納入眼簾的是底下的畫面:

你可以透過SSH連線的方式來連接,也可以點選上圖(2)的地方,產生連線所需的帳號密碼。

如果你用開發工具是Visual Studio,那你可以很大方的按下上圖(3)的位置,即可直接Clone該專案並且設定Mapping的用戶端資料夾位置,如果你用的開發工具並非Visual Studio,但也是知名的開發工具,則可點上圖(4)的下拉清單,看看是否也支援直接Clone:

從VS2017連接TFS/VSTS上的Git版控伺服器

當然,你也可以用類似我們開啟TFVC版控專案的方式,直接從Visual Studio當中建立連線,請注意一樣是選擇Connect to Project,在出現的畫面中找到要連結的專案與repository:

接著,成功的連上之後,在Team Explorer中一樣會出現讓我們選擇用戶端Mapping版控檔案的儲存位置,也就是你要把雲端的原始程式碼Clone到用戶端的哪個資料夾。

請選定資料夾後(下圖A),按下Clone鈕即可:

如果伺服器端已經有其它開發人員先前push上去的程式碼,會被一起下載下來,但由於我們是建立一個新的專案,因此目前還沒有任何檔案被Clone下來。

我們可以點選下圖A的New,建立一個新專案:

你會發現預設的資料夾,應該也是先前我們Clone時設定的資料夾,你可以直接按下OK,建立該專案。完成後,切換到Solutions Explorer視窗後,會看到類似底下這樣的畫面,這時請留意,和TFVC版控有些許不同:

你會發現,在上圖A的位置,標示出了有12個檔案已變更(或新增),你可以點選該數字,會出現Commit程式碼的畫面。

將程式碼Commit並Push到伺服器端

你會發現,在下圖A的地方,和TFVC版控類似,也會讓你輸入此次Commit的Comment,而B的地方有三個選項,分別是Commit All, Commit All & Push, Commit All & Sync:

所謂的Commit,和TFVC的Check-in不同,Commit並沒有將程式碼上傳到伺服器端,你需要手動Push,或直接選擇Commit All & Push,才會把程式碼確實的簽入到伺服器端。(如果你選擇Commit All & Sync,則會順便把其他人簽入到伺服器端的程式碼一起下載下來)。

Push之後,你同樣也可以在TFS/VSTS的站台上,透過Code → History看到程式碼版本出現了:

同樣上圖A的地方一樣有你Commit時候寫入的Comment,我們試著新增一個index.html,並修改Global.asax.cs檔案:

然後再Commit & Push一次,觀察伺服器端的結果,你會發現同樣的,在Code→History當中,可以看到這次Push上去的Commit,點選後也可以看到修改的時間並且對照出修改了哪些內容:

同樣的你也可以在Team Explorer選單中,透過Home按鈕,找到Sync或Branch指令,Visual Studio很貼心的讓大多數的功能都可以透過GUI來使用:

不管你用TFVC或是Git版控形式,在Visual Studio中都可以非常方便的使用,而TFS與VSTS也都同時支援TFVC和Git,到這個地步,開發人員的每一個專案再不上版控應該就說不過去了…

同場加映

 

------------------------------
本系列文章索引位於 http://studyhost.blogspot.tw/2017/02/the-devops-journey-index.html
相關教育訓練: http://www.studyhost.tw/NewCourses/ALM 
若這篇文章對您有所幫助,請點選這裡加入FaceBook專頁按讚並追蹤,也歡迎您幫我們分享出去,謝謝您的支持。