一年後的我想要什麼?

你的目標是什麼?

自由

什麼是自由?

  1. 情感上的自由
  2. 時間上的自由
  3. 經濟上的自由

經濟上的自由是一切的基礎

  1. 更多的收入
    1. 被動的收入
    2. 更高的薪資
  2. 更有成就感的工作
    1. 被同事尊重
    2. 被上司認可
    3. 受人歡迎
  3. 更多的選擇
    1. 技術提昇
    2. 領域擴展
    3. 人脈
  4. 健康的身體
  5. 更多享受生活

一年後的我想要什麼?

一年後的我想要什麼?

  1. 更高的薪資
    1. 維持現狀 K (x)
    2. 跳糟 K * 1.3 (x)
    3. 爭取加薪 K * 1.1
    4. 獎金與分紅
  2. 更有成就感的工作(什麼是成就感)?
    1. 擁有可以引以為豪的產品
      1. 拆解單體為服務導向
      2. 架構升級

    2. 分享與教學
      1. Blog
      2. 內/外部sharing
      3. 單元測試導入

  3. 更多的選擇
    1. 技術轉移
      1. Web 技術 轉移 為 Service導向技術
      2. container 技術
      3. cloud 技術

    2. 技術提昇
      1. .Net
      2. Infra
      3. Domain know how

爭取加薪 K * 1.1

而這些目標需要什麼才能爭取到?

分為三個面向,技術提昇、技術擴展與自我實現,

技術提昇

首先目前我擁有的技術能力有

  1. .Net solution的 Web Developer能力
  2. 同時兼顧有 DB 與 F2E 基礎進階能力

這與我過去的選擇有關, 一直以來都在 Web 深入研究
未來的一年仍要朝這個方向發展.
但是會改由 Web 導向轉變成服務導向,
而 Web 開發只是我本身所能提供給公司的一個服務而已,
跟著 .Net 的腳步我想把前台(包含大馬但不限於)昇級上一個版本,
包含目前使用的舊版 libary 與 Framework,
如此一來可以使用到新版 .Net 的語法,
同時也可以解開一些導入測試與微服務時遇到的困境.

現有能力列表

  1. C# & .Net Framework Solution (inculde Linq & Entity Framework )
  2. javascript (jQuery & Angular etc..)
  3. Database with Sql (MsSQL solution)
  4. Source Controle (Git)

略懂

  1. nodejs (with expressjs) & php(codeigniter) & ruby (RoR)
  2. Jenkins

技術的擴展

我認為公司的單體架構已經面臨到不得不拆的狀況,
公司也有意朝這個方向走那是最好不過的了,
但對現存的.NET 開發者而言,我認為人人都要有危機意識
我看到的現象

  1. 頁面會被CMS取代
  2. 大部份的API可以被Lambda取代
  3. 主流程的部份在跨國的目標下會逐步變成微服務

基於以上幾點, 除了.Net 的 solution 外,
更多情況是要使用別的 solution 或是混用,
對此我的視野必須有所提昇,

  1. Linux Bash
  2. Container 技術
  3. Cloud (AWS/Azure/GCP)
  4. Node.js
    在未來的一年開發流程或是維運流程會有很大很快的變化
    要多聽多看多想多問,公司有很多人才要儘可能的跟他們學習.

自我營銷

最後是自我實現的部份,
自我營銷是我很弱的一部份,
不善交際,不喜歡人群
這點我從去年就開始調整,
多參與公司內部的分享,不要害怕說錯
假裝自已是對的,再虛心接受別人的指點
不需要導師,因為人人都是我的導師
開始寫Blog並且貼到社群網站給人鞭
這是我目前的 https://blog.marsen.me

今年會繼續朝這個方向衝刺.
多分享 多犯錯 然後接受反饋學習.
兩個部份是我可以練習分享的機會
一個是測試的導入,
我們的遺留代碼,有很多可以分享的部份,
二是讀書會,
借由讀書會可以練習分享,
同時學習別人怎麼分享,
並且看完一本書,一舉數得.

具體主管可以幫助我的部份

  1. 加薪(沒有比這個更務實的了)
  2. 明確指出我的錯誤或是作的好的部份

Do more do faster

(fin)

2017年的學習回顧與展望

測試

參加了兩個活動,分別是單元測試這樣玩就對了,與測試即學習;

單元測試這樣玩就對了

最大的收獲是突破了寫測試的心魔, 在那之前總覺得 TDD 只是口號, 或是烏托邦的開發理想.
雖然之前也有花大錢出外受訓, 或是公司內部的內訓.
手上也有一些前輩的測試代碼, 但是就是沒有「感覺」,

1
2
3
不過仍然感謝前幾年的自已有將資源花在測試上面,
累積了兩年的測試經驗,
融會貫通卻只是一瞬間的事

最主要是講者展現了實務上面的需求與改進,
過去寫的加法運算, 或是吊人遊戲,
雖然也是從無到,也是先寫測試,
但或許是太過強調 TDD 太過強調從無到有
總與實際開發經驗相違背,
講者在過程中一句 「不要管先寫測試還是程式」
反而更貼近真實,先寫程式,再想想怎麼測試?
為了測試, 再重構, 逐步分解的過程令我大開眼界.

過去曾與人討論過 TDD 的議題,
總會得到「實務與理論有差別」、「其背景與環境因素不能寫測試」
這類似是而非的回答,當下我也無法辯駁
現在想起來只能莞爾.

測試趨動開發不等於一定要先測試,
特別是習慣於先寫產品程式的人,
不仿先寫產品程式, 再寫測試而趨動重構
當寫習慣了,知道哪些耦合會帶來重構的代價時,
再寫一次就人性就會自動迴避這些不好的 Patten .

別管順序,但是記得寫測試.

測試即學習

梅老師的課也是很毀三觀的,
老實說我跟本不知道他在幹嘛,
塔羅牌拿出來的時候,
我真的以為是美江再現(那個時候Seafood還沒有業力引爆( ・ω・)( ・ω・)( ・ω・))
那堂課的目標族群應該是 QA,
不過這個時代不應自我設限,
複習一下梅老師的分享,

探索測試

  • 儘可能的發散
  • 摹仿別人看事情的角度
  • 有意識的學習(mindful learning)
    • 記錄
    • 總結
    • 歸納問題的核心
    • 給它起一個名字(沒有專有名詞的話)
    • 有意識的逃離第一印象
    • 小心不經意的盲區(inattentional blindness)
    • 數量 X 練習 X 思考 X 學習 = 提昇

對我來說,給它起一個名字真是超級有用的,
新認識的朋友就叫作「王大棰」, 新的技術就叫「起司包」, 新的概念就叫「黑盒子」等…
不要過於專研於名詞, 用自已能理解的名字去框那個新事物的範圍,
最後再把名字用大家通用的名字取代掉就好了.
看了很多的方法論,會發現其實觀念沒什麼改變,
只是新的名詞會一直冒出來,
唯物主義、馬基維利主義、不擇手段、實用主義
改善、KANBAN、敏捷、精實 balabala…

“没有时间”- 完美的借口

2017 年影響我最深的一篇 Blog,
2012 年的文章, 篇幅也不長,
觀念也很簡單, 作就對了, 作壞了就丟掉再作一次,
很多的書都要我們刻意練習,
但是我們的時間哪有那麼多呢?
長大了之後,才來 1 萬小時的修練是不是太晚了呢?

下半年公司開始推行測試,
基層的工程師們其實反彈的聲音一直都在,
雖然一直有教育訓練與培養種子,
不過大多淪為口號的狀態,
我也只能用這篇文章作為勉勵.

其實現在的版本控制系統已經非常便利,
要建立一個 Sandbox 的分支,
在裡面嘗試各種可能性是非常容易的,
作壞了丟掉分支就好了,
身為專業的工程人員,測試是必須的,失敗也是,
在沙箱內失敗,其實是摔不疼的,勇敢嘗試吧.

生產力

2016 年是時間記錄的一年,
那 2017 年就是把記錄的時間,
轉換成生產力的一年.
幾本影響比較多的書,不過或許我仍需要二讀至三讀以上

  • 最有生產力的一年 → 時間 X 精力 X 專注力 = 生產力
  • GTD → 下一步要作什麼?
  • 軟技能 → 自我營銷

新的一年重心會放在習慣上面,
因為讓習慣趨動行為,
比起刻意遵循某些方法要好得多,
「刻意」太浪費精神力了,
下一步會如何呢?
希望能翻轉自已

其它

順利的帶媽媽出國了,或許對一些人來說不算什麼成就吧,
新的一年有些機會,去日本、以色列、俄羅斯、馬來西亞,
雖然八字還沒有一撇, 但至少會去一個地方吧…
其他的地方就只能見機行事了.

新年快樂
(fin)

[踩雷筆記] Visual Studio 2017 MSTest Framework 異常修正

應該要知道的事

  • 這是踩雷筆記
  • 2017 的筆記可能會隨時間變得沒有參考價值
  • Visual Studio 2017 的問題,並不一定適用其他版本
  • 最後面會有不定時補充

情境

載入測試時發生例外狀況
原本使用 Visual Studio 2015 建立的測試專案,
升級到 Visual Studio 2017 後, 發生以下錯誤

1
2
3
4
5
[2017/12/11 上午 02:09:59 Error] 測試探索程式 'SpecRunTestDiscoverer'
載入測試時發生例外狀況。例外狀況: 無法載入檔案或組件
'Microsoft.VisualStudio.QualityTools.UnitTestFramework,
Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
或其相依性的其中之一。 系統找不到指定的檔案。

導致結果

原本的測試數量為1942,變成459,遺失了7成5的測試案例.

  1. 測試專案會找不到測試,或是測試數量不正確.
  2. 可以使用 Visual Studio 2015 重新執行探測索測試,即可排除問題.

VS2015 已移除或未安裝該怎麼辦?

透過 MsTest 直接加入
Microsoft.VisualStudio.QualityTools.UnitTestFramework
的參考已經是舊的方法了,

在 Visual Studio 2017 建議的解決方案如下:

  • 移除方案中所有對 Microsoft.VisualStudio.QualityTools.UnitTestFramework 的參考
  • 透過 Nuget 安裝 MSTest.TestAdapter
  • 透過 Nuget 安裝 MSTest.TestFramework
  • 關閉 Visual Studio 2017
  • 移除 %temp%\VisualStudioTestExplorerExtensions內所有檔案
  • 重啟 Visual Studio 2017 並建置以觸發探索測試
    透過 Nuget 安裝 MSTest.TestAdapter/MSTest.TestFramework
    重啟 Visual Studio 2017 並建置以觸發探索測試

參考

(fin)

補充

  • 2018/06/02 :
    Visual Studio 2017 15.7.* 的版本之後 ,
    %temp%\VisualStudioTestExplorerExtensions 消失了 ,
    不過正常情況建置後 , visual studio 探索測試仍然可以正確找到測試。

[實作筆記] 單元測試與重構記錄(二) 發問篇

Q1 Controller 要測試嗎?

Logics in controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[Route("Member/Get/{Id}")]
public JsonResult GetMemberList(long Id, string cc = "f")
{
var cleanCache = false;
//// logics here
if (this.IsFromCompany() && cc == "t")
{
//// do something ...
}

try
{
var memberList = this.memberService.GetMemberList(Id, cleanCache);
//// logics here
if (memberList.Any())
{
//// do something ...
}
else
{
//// do something ...
}

return this.Json(result, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
//// logics here
//// do something ...
}
}

自問自答

我認為要,
但是對於 WebAPI 回傳的JsonResult或是ActionResult
需要轉形才能作驗証
可以考慮整合測試勝於單元測試,
Controller 的通常是面對 Client Side 的呼叫.

Q2 當 Controller 只有取資料的邏輯

No Logics in Controller

1
2
3
4
public ActionResult Index()
{
return this.Service.GetIndex();
}

Q3 當 Service 只有取資料的邏輯

No Logics in Service

1
2
3
4
public Member Get(long id)
{
return this.DataAccessor.GetMember(id);
}

Q3.自問自答

我認為不要,
要測試商業邏輯,不要在意覆蓋率

Q4. 當 Service 只有取 Catch 資料的邏輯

No Logics in Service , just call another service

1
2
3
4
5
6
7
8
9
10
11
public Member Get(long id)
{
var enableCache = true;
var result = this.CacheService.GetCacheData(
cacheKey,
() => {
return this.DataAccessor.GetMember(id);
},
enableCache
);
}

Q4.自問自答

同上,仍然不需要,
要測試商業邏輯,不要在意覆蓋率,
要注意的或許是CacheService.GetCacheData是不是有包測試 ?
一般來說,Cache 的功能很泛用,測試的報酬率很高

Q5. 承上,當邏輯存在 Func 參數之中?

Logics in Func

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Member Get(long id)
{
var enableCache = true;
var result = this.CacheService.GetCacheData(
cacheKey,
() => {
//// logics here
if(id > 9487)
{
return this.MemberAccessor.GetMember(id);
}else
{
return this.MemberV2Accessor.GetMember(id);
}

},
enableCache
);
}

Q5.自問自答

暫時無解,
或許是這樣 Pattern 不適合測試,需要調整架構嗎?
為了測試多包成一個公開方法,反而失去匿名函數的彈性優點,
不在匿名函數內寫邏輯更不合理,待求解答

Q6.當邏輯在 DA 層或 ORM 的 Query 中要如何測試?

Logics in ORM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上略...
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
using (Entities context = Entities.CreateNew(isReadOnly: true))
{
//// logics here
var query = from a in context.Activies.Valids()
where a.Activies_StartDateTime <= startTime &&
a.Activies_EndDateTime >= now &&
a.Activies_ShopId == shopId &&
a.ActiviesCondition.Any(i => i.Activies_ValidFlag
&& TypeList.Contains(i.Activies_TypeDef))
select a;
}
}

Q6.自問自答

不適用單元測試,應該整合測試作包覆

Q7. 當邏輯在 MappingProfile 該如何測試?

Logics in MappingProfile

1
2
3
4
5
6
7
8
protected override void Configure()
{
Mapper.CreateMap<PageEntity, UserPageEntity>()
.ForMember(i => i.Id, s => s.MapFrom(i => i.User_Id))
.ForMember(i => i.Title, s => s.MapFrom(i => i.User_Name))
.ForMember(i => i.PageName, s => s.MapFrom(i => i.User_Name + i.User_LastName))
.ForMember(i => i.LightBox, s => s.MapFrom(i => i.User_Sex == "male" ? true : false));
}

Q7. 自問自答

要作測試,檢查欄位 Mapping 是否正確,
但實務上若重用性不高,寫 MappingProfile 不如直接在代碼內轉換.
可以少寫 MappingProfile 的測試.

待解答…

(fin)

[實作筆記] AWS EC2開機筆記

應該知道的事

  • 這個是教育訓練的筆記
  • 使用 web console 建立 ec2
  • 使用 cli 建立 ec2
  • 2017 的筆記可能會隨時間變得沒有參考價值
  • 關鍵參數都打馬賽克,沒有牽扯到 EBS/S3/VPC
  • 對你可能沒有幫助

Web Console

  1. Login AWS

  2. 進入 EC2
    進入EC2

  3. Launch Instance
    Launch Instance

  4. 選擇 AMI (Amazon Machine Image )
    選擇 AMI

  5. 選擇 Instance Type (有錢隨便選,沒錢選 t2.nano)
    還擇 Instance Type

  6. 設定 Instance Details
    設定 Instance Details

  7. 如果想在開機的時候自動安裝一些程式,可以在 Advanced Details 加語法
    windows AMI 請用 Powershell
    Advanced Details

  8. 加硬碟
    加硬碟

  9. 加 tag
    加Tag

  10. 設定 Configure Security Group
    Configure

  11. 預覽與啟動
    預覽與啟動

  12. 最後一步,選擇 key-pair
    Key-Pair

CLI command

1
aws ec2 run-instances --image-id ami-da9e2cbc --count 1 --instance-type t2.nano --subnet-id subnet-cxxxxxxx --user-data file://userdata.sh --tag-specifications "ResourceType=instance,Tags=[{Key=Environment,Value=AWS-Training},{Key=Name,Value=AWS-Training_MarkLin}]" --security-group-ids sg-XXXXXX --key-name marktest.japan.training

參考文章

(fin)

[實作筆記] 單元測試與重構記錄(一)

前情提要

有幸參與了一個跨國的專案,
為了快速上線,決定將整套原本在台灣程式碼搬移到跨國專案上,
上線後再依使用者的需求調整開發功能,
而在搬移的過程中,有需多模組並未開啟。

現況

遺留代碼 → 跨國遇到的問題,
主管要求用複制整個台灣的環境、流程與代碼到國外的環境之上,
但是要求 One Code Base:

  1. 台灣的情境不一定適合國外環境
  2. 修改功能與台灣邏輯抵觸時,台灣 PM 不一定允許修改
  3. 到處寫例外 if、switch 處理 台灣其它國 的問題
  4. 完整重建整套系統非常費功,沒有資源時會犧牲子系統,導致國外環境部份功能無法使用或是必須與台灣共用

用明朝的劍,斬清朝的官

實務需求

將本來跨國未開啟的折扣活動模組打開,
簡單的流程大致如下:
購物車 → 取得購物車資料 → 折扣活動 → 計算

實務上,整個流程作了許多事
整個流程作了許多事

應該說作了太多事.
作了太多事

程式碼有壞味道,卻不能修改(重構).
因為沒有單元測試保護.

單一的 Process,複雜度過高的方法(12)

CalculateShoppingCartPromotionDiscountV2Processor.Process()

複雜度過高的方法

目標與執行順序

  1. 由 PM 或 QA 補足整合測試情境到足夠
    • 由實務上的需求來認定
  2. 刪除台灣的測試
  3. 解析 CalculateShoppingCartPromotionDiscountV2Processor
  4. 補上單元測試
    • Code Coverage(測試覆蓋率)
  5. 重構

最終的目標是重構

  • 心態:沒有時間,完美的借口
  • 重構前要先作整合測試
  • 現有的整合測試的缺陷
    1. 測試項目不符合馬來西亞現狀
    2. 測試項目未處理多語系
    3. 測試項目未處理小數點
    4. 測試項目難以閱讀
    5. 測試項目有重覆的覆蓋範圍
  • RD 與 PM 與 QA 合作

UAT 讓「人」讀得懂

原本的 UAT (RD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
場景: case37:商品活動+全店活動(有排除);宅配,運費50元,滿350免運
.全店活動 / 有排除商品;滿額打折,多階(滿第1階),跨溫層
.折扣條件:滿199元,打95折 / 滿299元,打89折 / 滿399元,打84折
.商品活動;滿件折現,單階,跨溫層
.折扣條件:滿2件,折45元
假設 購物車中溫層"Freezer"商品為
| SalePageId | SaleProductSKUId | Price | Qty |
| 50 | 50 | 75 | 1 |
| 27 | 27 | 66 | 2 |
並且 購物車中溫層"Refrigerator"商品為
| SalePageId | SaleProductSKUId | Price | Qty |
| 26 | 26 | 55 | 2 |
並且 購物車中溫層"Normal"商品為
| SalePageId | SaleProductSKUId | Price | Qty |
| 25 | 25 | 2 | 2 |
並且 活動"1"範圍設定為
| TargetType | TargetIdList |
| Shop | 1 |
並且 活動目標排除商品頁為
| PromotionId | TargetExcludeSalePageList |
| 1 | 50 |
並且 現折活動"1"的折扣為
| Id | TypeDef | TotalPrice | DiscountTypeDef | DiscountRate |
| 1 | TotalPriceV2 | 199 | DiscountRate | 0.95 |
| 2 | TotalPriceV2 | 299 | DiscountRate | 0.89 |
| 3 | TotalPriceV2 | 399 | DiscountRate | 0.84 |
並且 活動"2"範圍設定為
| TargetType | TargetIdList |
| PromotionSalePage | 0 |
並且 活動目標商品頁為
| PromotionId | TargetSalePageList |
| 2 | 50,25,26,27 |
並且 現折活動"2"的折扣為
| Id | TypeDef | TotalQty | DiscountTypeDef | DiscountPrice |
| 4 | TotalQtyV2 | 2 | DiscountPrice | 45 |
當 計算活動折扣
那麼 購物車商品折扣後為
| SalePageId | SaleProductSKUId | PromotionDiscount |
| 50 | 50 | -12 |
| 27 | 27 | -25 |
| 26 | 26 | -19 |
| 25 | 25 | 0 |

「人」寫的 UAT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
場景: 商品有兩檔活動,全店活動與商品活動;
.第一檔是全店活動 / 排除商品B;滿額打折,
.折扣條件:滿10元,打95折 / 滿20元,打89折 / 滿30元,打84折
.第二檔是指定商品;滿件折現,單階,指定商品A 、商品B
.折扣條件:滿2件,折3元

當 購物車中的商品為"商品A 與商品B"
| Title | SalePageId | SaleProductSKUId | Price | Qty |
| 商品A | 25 | 25 | 7.45 | 2 |
| 商品B | 26 | 26 | 4.45 | 2 |
並且 第"1"檔是全店活動 ,排除以下商品
| Title | SalePageId |
| 商品A | 26 |

而且 第"1"檔折扣條件是"滿額打折,滿10元,打95折 / 滿20元,打89折 / 滿30元,打84折",如下
| Id | TypeDef | TotalPrice | DiscountTypeDef | DiscountRate |
| 1 | TotalPriceV2 | 10 | DiscountRate | 0.95 |
| 2 | TotalPriceV2 | 20 | DiscountRate | 0.89 |
| 3 | TotalPriceV2 | 30 | DiscountRate | 0.84 |

並且 第"2"檔是指定商品,指定商品如下
| Title | SalePageId |
| 商品A | 25 |
| 商品B | 26 |
| 商品C | 27 |
| 商品D | 50 |

而且 第"2"檔折扣條件是"滿件折現,滿2件,折3元",如下
| Id | TypeDef | TotalQty | DiscountTypeDef | DiscountPrice |
| 4 | TotalQtyV2 | 2 | DiscountPrice | 3 |

當 計算活動折扣

那麼 購物車商品折扣金額及折扣後小計為
| Title | SalePageId | Price | Qty | PromotionDiscount | TotalPayment |
| 商品A | 25 | 7.45 | 2 | -2.55 | 12.35 |
| 商品B | 26 | 4.45 | 2 | -1.11 | 7.79 |

與 PM 及 QA 討論後 , 寫的 UAT 可閱讀性提高了
這裡用到了一些小技巧 , 讓 Cucumber 文件的可讀性更高
而不會有太多的重複方法 , 比如說把描述性的文字當作參數傳遞
實際上測試不會使用到這些變數 ,但是可以增加可讀性 .

Skip 台灣測試

因為已經有了跨國所需要的測試 ,
台灣的測試便可以退場了.
實際上也不符合現況, 如多語系、時差與小數點等問題

解析 CalculateShoppingCartPromotionDiscountV2Processor

解析

  1. 無折扣的情境
  2. 新舊相容的情境
  3. 排序
  4. 計算折扣金額
  5. 看見相依
    1. 程式碼中有 new 別的 class 的部份
    2. 程式碼中有使用靜態方法的部份

補上單元測試

最簡單的重構,就是將整個方法內的四個邏輯
拆成四塊個子方法,並為他們加上單元測試.
修改的過程,如果有紅燈就要修改成綠燈,
而整個成品要保證整合測試與單元測試都是綠燈.

此外,重構的過程中如果過到靜態方法,
或是 new 新物件, 都很有可能是種相依,
可以透過一些方法作解耦,
參考之前的文章單元測試這樣玩就對了

重構

最後一步就是大膽的重構了,
有了測試作保護,
可以作更大範圍的重構,
如下圖示,這裡揭露了在台灣原有的繼承結構,
而紅色的部份是在跨國用不到的類別.
類別

下一步,待續…

(fin)

20171023本周要聞/心得/學習擷錄

  1. Docker 宣布拥抱 Kubernetes

    1
    2
    3
    4
    Docker Swarm与整个Docker平台紧密集成,然而并非所有人都愿意选择Swarm。
    Hykes表示,默认的Swarm已经限制了Docker用户的完整体验,
    为此,Docker公司计划提供一个无缝平台,同时支持包含Swarm和Kubernetes集群的异构部署。
    Docker企业版(EE)将很快为Kuberenetes和Swarm提供全套Docker管理服务
  2. 免費字哪裡找?使用google font

  3. 使用 TypeScript 開發 nodejs 發生以下錯誤(使用 gulp 編譯成 js)

    1
    error TS2693: 'Promise' only refers to a type, but is being used as a value here.

    安裝 @types/es6-promise 以解決問題

    1
    npm i --save-dev  @types/es6-promise
  4. 使用 netstat -ano 指令在 windows 上查詢佔用的 port 與 PID

  5. 執行 npm ls moduleName 可以列出目前專案所以相依該模組的模組

  6. 執行 npm update 更新目前專案的模組

  7. package.json裡面的的節點dependencies放的是與專案相關的模組,
    devDependencies放的是與開發相關的模組。ex:gulp,
    請考慮你使用的模組,是為了開發?還是產品真的會用到。

  8. 想法

    • 懶人包應是協助跨越門檻與看見全貌的工具,
      雖然難以避免代入個人的價值觀,但是刻意為之是不好的。
    • 自我批判是一種建立反饋的最好方式,比起以公司\社群\世界最好的人要求自已,
      以最好的自已去要求自已理應該最恰當的,錄影、看鏡子之類的方式… 。
  9. [叫人意想不到的激勵科學 (TED 中英文字幕)] (https://www.youtube.com/watch?v=rFVhkIrVDzM)

    1. 自主性
    2. 掌握度
    3. 使命感

(fin)

[閱讀筆記] 異數

心得小結(2017.一讀)

作者認為成功的主要原因是優勢的累績與努力,
我們常說時間花在哪裡,成就就在哪裡,
但是許多傑出的例子(莫札特與比爾。蓋茲),
除了努力外,還有幸運的(?)際遇,
在很小的時候就開始邁向傑出,
這讓我想到福原愛;從小就接觸桌球又有母親(導師)親自調校,
但作者最終歸向時代與文化,兩個不可控的因素。
努力只能讓你成為比一般人好的人,
而加上際遇(時代與文化),才能成為傑出(ex:世界前 100 之類),
相比「我比別人更認真-刻意練習.讓自己發光」,
書比較強調導師與信念(熱情)的重要性,
異數比較偏向命定論。

並沒有很推薦,但是書中的馬太效應,
以及透過學習別的語言來改變文化帶來的
值得反思 .

筆記

ch1 好上加好的馬太效應

  1. 成功是優勢的累積
  2. 我們把成功與個人特質畫上等號,其他人因而失去出頭的機會。
    我們制定出來的規則反而壓抑成就,我們太早宣布某些人是失敗者。

ch2 一萬個小時的努力

  1. 他們的成功不完全是自已打造出來的,也是他們生長的時代造就的。

ch3 天才的迷思

ch4 天才的迷思.II

  • 才智和成就沒有絕對關係
  • 抽象智能與實用智能
    • 實用智能:社交常識與處事能力(後天習得,家庭),知道在什麼時候用什麼方式以及說什麼,以達到最大效果
  • 階級的文化優勢
    • 勞動階級和貧窮人家的孩子,保守不信任別人,厭惡權威,拙於交際

feedback:

  1. 千里馬常有,伯樂不常有。
  2. 我是勞動階級和貧窮人家的孩子
  3. 人脈的重要性。
  4. 如何與權威人士交際?

ch5 猶太律師的啟示

  1. 什麼都作,有生意上門就好了
  2. 機會其實是隱藏在逆境當中
  3. 你的父母是做什麼的,和你日後的成就大有關係
  4. 只要夠聰明就夠了

feedback:

  1. 七年級最慘的是哪一年次?
  2. 生逢其時 vs 生不逢時

ch 6 以血還血

ch 7 空中危機

  1. 文化的影響
  2. 以大韓航空空難為例-文化的包袱
  3. 用語言改變文化的包袱(ex:英文)

ch 8 稻米文化與數學能力

ch 9 知識力學校

後記 從牙買加到加拿大

  1. 亞洲數字發音較有規則且合乎邏輯
  2. 成功者的共通點就是努力

書單

  1. 意外的百萬富翁(Jobs 傳記)

其它

  1. 廖月娟的翻譯很不錯,感覺不出來是在讀外國人寫的書。
  2. 台灣財富排名 690 萬人是月光族 230 萬人有超 3 千萬資產

(fin)

使用 DateInterceptor 攔截SQL 語法

前言

SQL Compatibility Level 對 MsSQL 時間查詢的影響這篇文章裡,
遇到了一個令難以處理的問題, 簡單的複述一下,

  1. 我的資料表以一個 datetime 欄位當作 PK
  2. 我的 SQL DB 版本為 2016 以上 (Compatibility Level 預設值為 130)

這個時候我所有的 Entity Context 更新語法在呼叫 SaveChange 時會拋出錯誤;
原因在於 EF 所產生的 SQL Query 語法 , 會以 PK 值作為查詢條件,
而在這個時候 查詢時間的條件會精準到微秒(μs=1/1000000 秒),
但是 datetime 欄位只會記錄到豪秒(ms=1/1000 秒),
於是精準不足的部份就會補 0 ,
EX:
2017-09-24 11:55:35.37200002017-09-24 11:55:35.372
這會導致查無資料進而引發 dbupdateconcurrencyexception

解決方針

修改資料欄位

將資料庫的欄位 datatype datetime 改成 datetime2 ,
這或許是最理想的解法了, 你不需要更動程式碼,
而且會提昇你資料的時間精準度.
不過實務上,你必須考慮到你是否有 dba 的權限與即有的資料量,
當產品的核心功能依賴著這張表的時候且有巨量資料存在時,
更新欄位的資料型態的衝擊與風險或許是難以承受的.
更不用說還要考慮到整個 DB Server Cluster 的架構之類的問題.

修改 SQL Server Compatibility Level 從 130 至 120

非常不建議的作法, 除了要考慮上述權限、衝擊與風險的問題外,
如果降轉 Compatibility Level
或許會使得其他有使用到 datetime2 型態的資料欄位發生異常,
或是是失去原本精度的意義.

使用 DateInterceptor 攔截 SQL 語法

在考量上述兩種情況, 為了不增加DBA 的工作量無謂的風險與權責問題,
(其實是實務上我沒有 DB Server 的異動權限),
我們可以透過 IDbInterceptor 來欄截 Entity Framework 對 DB 存取時執行的 Query

以下是個簡單的範例,

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DateInterceptor : IDbInterceptor
{
public void ReaderExecuting(DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
var dateParameters = command.Parameters.OfType<DbParameter>()
.Where(p => p.DbType == DbType.DateTime2);
foreach (var parameter in dateParameters)
{
parameter.DbType = DbType.DateTime;
}
}
}

我們實作了一個 IDbInterceptor 的類別,
用來將 datetime2 的資料型別轉型成 datetime,
接下要將它掛載在 Entity Context 之中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public partial class EF6Entities : DbContext
{
public EF6Entities()
: base("name=EF6Entities")
{
DbInterception.Add(new DateInterceptor());
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}

public virtual DbSet<BatchUploadData> BatchUploadData { get; set; }
}

如此一來就會以 datetime 的精準度產生 SQL Query

參考

(fin)

SQL Compatibility Level 對MsSQL時間查詢的影響

大綱

這次透過 Entity Framework(EF5)作了一個簡單的資料庫更新,
恰巧的是這次更新的 Table 因為某些需求,使用 datetime 作為 Key 值.
進而引發一連串的錯誤, 最後才找到 SQL Compatibility Level 對 MsSQL 時間查詢影響.

正式環境 SQL 版本 13.0.4422.0

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
public void UpdateBatchUploadData(BatchUploadDataEntity batchUploadDataEntity)
{
using (WebStoreDBEntitiesV2 context = this.LifetimeScope.Resolve<WebStoreDBEntitiesV2>())
{
var item = (from batchUploadData in context.BatchUploadData.Valids()
where batchUploadData.BatchUploadData_Id == batchUploadDataEntity.BatchUploadData_Id
select batchUploadData).FirstOrDefault();

this.MapBatchUploadData(batchUploadDataEntity, item);

context.SaveChanges();
}
}

如上面程式所示, item 是透過 Key 值 BatchUploadData_Id 取回來的物件.
MapBatchUploadData 是一段簡單的程式碼,
單純的將 batchUploadDataEntity 的值 mapping 到 item
再呼叫 SaveChanges , 卻引發了 dbupdateconcurrencyexception

錯誤畫面

錯誤訊息

1
2
存放區更新、插入或刪除陳述式影響到非預期數目的資料列 (0)。
這些實體載入之後可能被修改或刪除了。請重新整理 ObjectStateManager 實體。

這一段訊息的意思就是: Entity Framework 預期更新了0筆資料,與它所預期的不符, 所以拋出錯誤。

原因

透過用Sql Profiler我們錄製到了以下的 SQL

1
2
3
4
5
6
exec sp_executesql N'update [dbo].[BatchUploadData]
set [BatchUploadData_StatusDef] = @0, [BatchUploadData_UpdatedTimes] = @1, [BatchUploadData_UpdatedDateTime] = @2
where (([BatchUploadData_Id] = @3) and ([BatchUploadData_CreatedDateTime] = @4))
select [BatchUploadData_Rowversion]
from [dbo].[BatchUploadData]
where @@ROWCOUNT > 0 and [BatchUploadData_Id] = @3 and [BatchUploadData_CreatedDateTime] = @4',N'@0 varchar(30),@1 tinyint,@2 datetime2(7),@3 bigint,@4 datetime2(7)',@0='ProcessFailed',@1=1,@2='2017-09-16 11:29:35.3720061',@3=52,@4='2017-09-05 18:53:36.3530000'

請注意到 @4 datetime2(7) … @4=’2017-09-05 18:53:36.3530000’
如果將 datetime2(7) 改為 datetime或是將查詢語句改為 @4='2017-09-05 18:53:36.353 就能正確更新資料.

本機實測 (SQL 版本 12.0.4459.0)

透過本機寫了一小段的 SQL 作測試,

竟然不會有問題!!!

這跟 SQL Compatibility Level 有關,
mssql 2014 預設是 120, 2016 預設是 130,
Datetime2 在 120 跟 130 的結果會不一樣.

解決方法

主要的查詢與更新 SQL 是 Entity Framework 產生的,
所以我無法透過修改 SQL 的方式解決這個問題,
而正式環境的 SQL Compatibility Level 調整將會牽一髮動全身
且基於版本演進, 往新的版本靠攏是合理的選擇
暫時的解法是透過修改 edmx ,
不讓 datetime 作為整個 table 的 Key 值.
較好的解法是升級 Entity Framework
透過 Entity Framework 的機制, 指定查詢時間的精準度.
實作的部份未來再補上.

參考資料

(fin)