Quantcast
Channel: 黑暗執行緒
Viewing all 2458 articles
Browse latest View live

VS2012 JavaScript IntelliSense會忽略底線起首的屬性

$
0
0

我寫了一個CRUD輔助程式庫,內建自動抓取ViewModel進行JSON序列化的功能,其中有條"序列化時排除ViewModel中名稱為底線開頭屬性"的慣例(Convension)。前陣子接到同事回報,配合此慣例宣告ViewModel,卻發現底線起首的屬性會被Visual Studio 2012的JavaScript IntelliSense無視。

寫一小段程式,馬上獲得驗證:

一直沒留意VS2012的JS IntelliSense有此特性,查詢MSDN文件後獲得證實(在Handling IntelliSense events小節)。原來這是JS Intellisense強大擴充能力的一環,過濾底線開頭屬性的程式被放在VS2012的安裝目錄JavaScript\References\underscorefilter.js。

既然過濾行為由underscorefilter.js控制,要修改並非難事。不過,屬性名稱以底線開頭在JavaScript慣例隱含"私有"、"限內部使用"的意義,不應開放外界呼叫,因此不列在自動完成清單十分合理! 不想打破這個立意良好的設計,最後決定調整程式庫慣例,改成"序列化時排除名稱為$開頭屬性"!

案外案: 約定以$為屬性字首慣例,在C#端再踢到小石頭一顆: .NET識別名稱命名規則只允許字母或底線當字首,無法在C#中使用$作為屬性名稱字首與JavaScript端對應。最後的解決做法是在C#命名為"priv_",JSON序列化送到JavaScript端後再將"priv_Boo"改成"$Boo"。


IE9 Bug-滑鼠移過導致高度增加

$
0
0

日前曾遇此蟲,爬文解決而未寫KB。今日同事再遇,腦中只餘殘存印象,戳力苦思蒐羅,方復拾回,嗔甚,為文誌之以杜後患。

網友Brian Richards對這個問題做了清楚的分析,也提交到MS Connect,其發生條件包含:

  • DIV設為overflow: auto且指定寬度
  • DIV內含TABLE,寬於DIV而產生水平捲軸
  • TD中元素使用CSS :hover進行樣式變化

以下範例可重現問題:

<!DOCTYPEhtml>
<html>
<head>
<metacharset=utf-8/>
<title>IE9 Bug</title>
<style>
    .lnk { cursor: pointer; color: white; }
    .lnk:hover { color: yellow; }
</style>
</head>
<body>
<divclass="container"
style="overflow: auto; width: 200px; background-color: gray;">
<tablestyle="width: 300px">
<tr><td><spanclass="lnk">Line1</span></td></tr>
<tr><td><spanclass="lnk">Line2</span></td></tr>
<tr><td><spanclass="lnk">Line3</span></td></tr>
</table>
</div>
<div>Bottom of Page</div>
</body>
</html>

示範如下: 線上展示(需用IE9檢視)

解決方法: DIV加上CSS min-height: 0%即可避免。線上展示

PS: 此問題在IE10已修正,但IE9應該是不打算修了。IE Team心理應該也悶,Chrome、Firefox Team表示: 舊版是什麼? 能吃嗎?

【茶包射手日記】ASP.NET MVC 4專案4.5 Framework轉4.0

$
0
0

記錄在Windows Server 2003 IIS6部署ASP.NET MVC 4專案的經驗。

部署第一步是複製檔案、設定Web Application、萬用字元應用程式對應等。(相關眉角可參考保哥的ASP.NET 4.0 安裝在 IIS6 最常遇到的四個問題)

第一枚茶包現身! 當初新增專案時一時不察,選了.NET Framework 4.5,web.config有兩行設定:

<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />

移到Windows 2003立即引爆。由於Windows 2003不支援.NET 4.5,只好乖乖地開Visual Studio將MVC專案降版為.NET 4.0。web.config內容被Visual Studio改成:

<compilation debug="true" targetFramework="4.0"/>
<httpRuntime/>

重新部署檔案,正期待看到網頁,另一枚茶包冒出來澆我一頭冷水:

[FileNotFoundException: Could not load file or assembly 'System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.]
   System.Web.Http.GlobalConfiguration..cctor() +0

[TypeInitializationException: The type initializer for 'System.Web.Http.GlobalConfiguration' threw an exception.]
   System.Web.Http.GlobalConfiguration.get_Configuration() +14
   MyWeb.Areas.HelpPage.HelpPageAreaRegistration.RegisterArea(AreaRegistrationContext context) +94
   System.Web.Mvc.AreaRegistration.CreateContextAndRegister(RouteCollection routes, Object state) +104
   System.Web.Mvc.AreaRegistration.RegisterAllAreas(RouteCollection routes, IBuildManager buildManager, Object state) +195
   System.Web.Mvc.AreaRegistration.RegisterAllAreas(Object state) +27
   MyWeb.WebApiApplication.Application_Start() +12

推測是Visual Studio的4.5到4.0轉換不夠完整所致,決定另開一個.NET 4.0 MVC4專案比對最直接有效。很快找到一項明顯差異:

.NET 4.5轉.NET 4.0的MVC4專案,System.Net.Http.dll的參照來源是:
C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 4\Assemblies\System.Net.Http.dll

而原生.NET 4.0 MVC4專案,System.Net.Http.dll則來自NuGet:
..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll

由於System.Net.Http在.NET 4.5才納入BCL,猜想就是造成在.NET 4.0無法運作的元凶。問題專案先移除System.Net.Http參照,重新取用NuGet提供的版本,重新編譯、發佈後bin目錄多了System.Net.Http.dll與System.Net.Http.WebRequest.dll兩個檔案,再試一次,問題排除!!

ASP.NET MVC Filter練習-限定本機存取

$
0
0

在ASP.NET MVC專案新增了開發偵錯專用的Controller,某些Action想限定從localhost存取,以免遭到誤用。逐一在Action加入檢查IP邏輯是種做法,但如此有點浪費ASP.NET MVC強大的擴充性,就好比提著子彈上膛的M16步槍上戰場,不扣板機卻拿槍托狂敲敵人的頭,不免有暴殄天物之憾。

ASP.NET MVC有個Filter機制(中文翻成篩選器),在Filter可自訂執行Action時要一併觸發的邏輯,為Action加上[FilterName] Attribute,便可在該Action之前或之後插入自訂邏輯。這種概念很適合用來實現Log記錄、權限控管、Exception處理... 等等通用性任務。而IAuthorizationFilter介面專司權限管控,符合IP篩選的安全性質,因此我的構想是寫個類別,實做IAuthorizationFilter介面的OnAuthorization()方法,由傳入的AuthorizationContext取得UserHostAddress判斷來源IP,若HttpRequest不是來自本機(localhost)即拋出錯誤,就實現了Action只開放本機存取的效果。

FilterAttribute完成後,任何Action只要加上該FilterAttribute宣告,就會自動套用上述檢查限定本機存取,非常簡便易用。

以下是完整程式範例,為了增加應用彈性,我特別再抽出一層AllowedIpOnlyAttribute,宣告時傳入允許存取的IP清單;而LocalhostOnlyAttribute繼承AllowedIpOnlyAttribute,將允許IP寫死::1(IPv6)及127.0.0.1,成為AllowedIpOnlyAttribute的特例,做到限定本機存取。如此一魚兩吃,一次獲得兩種Filter,適用於不同情境。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace MyWeb.Models
{
publicclass AllowedIpOnlyAttribute : FilterAttribute, IAuthorizationFilter
    {
privatestring[] ipList = newstring[] {};
//建構式接收以逗號或分號分隔的IP清單,限定存取來源
//TODO: 如要方便事後修改,可擴充成由config讀取IP清單,但會增加被破解風險
public AllowedIpOnlyAttribute(string allowedIps)
        {
            ipList = allowedIps.Split(',', ';');
        }
#region IAuthorizationFilter Members
publicvoid OnAuthorization(AuthorizationContext filterContext)
        {
//實作OnAuthorization,當來源IP不在清單上,彈出錯誤
string clientIp = filterContext.HttpContext.Request.UserHostAddress;
if (!ipList.Contains(clientIp))
thrownew ApplicationException("Disallowed Client IP!");
        }
#endregion
    }
//限定本機存取為AllowedIpOnlyAttribute的特殊情境,限定IP=::1或127.0.0.1
publicclass LocalhostOnlyAttribute : AllowedIpOnlyAttribute
    {
public LocalhostOnlyAttribute()
            : base("::1;127.0.0.1")
        {
        }
    }
}

接著,見識Filter便利性的時刻來了,在Action加上AllowedIpOnly或LocalhostOnly,Action立刻變成限定特定IP或本機IP才能使用! 很方便吧?

        [AllowedIpOnly("192.168.1.100")]
public ActionResult IpOnly()
        {
return Content("IpOnly");
        }
        [LocalhostOnly]
public ActionResult LocalhostOnly()
        {
return Content("LocalHostOnly");
        }

ASP.NET MVC路由練習-API分版

$
0
0

手邊ASP.NET MVC專案有個隱藏需求,預計上線不久要推出新版,有一段時間新舊版本並存。有幾個供AJAX呼叫的API性質Controller,希望未來出新版時名稱能沿用,不要弄出BooV1Controller、BooV2Controller這種名字,最好在URL路徑加上v1/v2等就能搞定,例如:

第一版: ~/api/v1/boo/action
第二版: ~/api/v2/boo/action

在ASP.NET MVC專案處理此類路徑問題,我想到兩個選擇: 一個是利用Area機制、另一個則是直接用Route對應。考量Area切割範圍涵蓋Controller、Model及View,我只想區分Controller版本,搬出Area有點殺雞用牛刀,就用路由解決吧!

構想如下,ASP.NET MVC專案在Controllers下新增V1及V2兩個資料夾,其中各放入一個Controller類別,由於Namespace不同(Mvc4Lab.Controllers.V1 vs Mvc4Lab.Controllers.V2),兩個Controller可使用相同命名,都叫BooController。

我們都知道ASP.NET MVC是"以習慣取代配置"(Convention over Configuration),會試圖從URL中找出Controller名稱,主動尋找專案中同名的"***Controller類別"處理該URL請求。當同一專案中出現相同名稱Controller,得額外提供路由線索,ASP.NET MVC才能正確解析。

原以為得費點手腳,沒想到MapRoute()設想很周到,只需在設定路徑時額外傳入Namespace參數(支援字串陣列,限定只在指定命名空間尋找Controller),便能做到不同URL格式使用同名Controller的效果。

在App_Strat/RouteConfig.cs中加兩ApiV1, ApiV2兩則額外路徑,大功告成,就這麼簡單。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace Mvc4Lab
{
publicclass RouteConfig
    {
publicstaticvoid RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "ApiV1",
                url: "api/v1/{controller}/{action}",
                namespaces: newstring[] { "Mvc4Lab.Controllers.V1" }
            );
            routes.MapRoute(
                name: "ApiV2",
                url: "api/v2/{controller}/{action}",
                namespaces: newstring[] { "Mvc4Lab.Controllers.V2" }
            );
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", 
                    id = UrlParameter.Optional }
            );
        }
    }
}

不過要提醒,由於ASP.NET MVC下所有的Request都要先進行路由解析再決定處理方式,切忌把路徑設定規則搞得太多太複雜,以免拖累系統效能。

【笨問題】紅杏出牆的internal類別

$
0
0

以下的.NET專案,有什麼不合理之處?

.NET開發者都知道,.NET有四種存取層級: public、private、protected及internal。public是公開對外、private只限同類別內存取、protected對繼承的子類別開放、而internal則只對同一Assembly內的其他的型別公開。在接手維護的專案看到如上述圖片的奇蹟: 有顆DLL宣告為internal的類別,卻大大方方地在另一顆DLL登場,如同public類別一樣被宣告使用,跨越了Assembly的藩籬,顛覆我對internal的認知,目瞪口呆之餘脫口而出 -- 花惹發!

以為是什麼密技魔法,追了好一陣子才想起,啊! Friend Assembly~

這是.NET 2.0起就支援的特性,為Assembly加上[assembly: InternalsVisibleTo("AnothorAssembly")],AnotherAssemly就會被當成自己人,允許存取標為internal的型別及成員。這個技巧常被應用在單元測試,解決測試程式無法存取internal成員的困擾。依據MSDN的說法,Friend Assembly主要有兩種用途:

  • 在單元測試期間,測試程式碼在另一個組件中執行,但是需要存取所測試組件中的成員,此時可以將該成員標記為 Friend (Visual Basic) 或 internal (C#)。
  • 您在開發類別庫時,類別庫的附加功能包含在不同組件中,且需要存取現有組件中的成員,此時可以將該成員標記為 Friend (Visual Basic) 或 internal (C#)。

這回遇到的顯然是後者。寫.NET程式十年有餘,卻沒見過野生的Friend Assembly,初見大驚小怪,有失老鳥的端莊儀態,見笑了。也再次證明  -- 學海無涯啊~~~

KO範例29 - ko.computed隨堂測驗

$
0
0

專案出現一個ko.computed()應用實例,剛好可以考驗開發者對ko.computed()了解是否透徹。各位同學,準備接招!

需求是這樣的,有A、B、C三個欄位,若使用者修改A或B,C需更新為A + "-" +B;若使用者直接修改C,則以其輸入字串為主,忽略前述由A/B計算C的邏輯。另外,若由Server端讀取,ViewModel要更新為由資料庫讀取所得的A、B和C。

<!DOCTYPEhtml>
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>KO範例29 - ko.computed隨堂測驗A</title>
<style>
        .disp
        {
            margin-top: 5px;
            width: 100px;
            border: 1px solid gray; 
            padding: 5px;
        }
</style>
</head>
<body>
<div>A: <inputtype="text"data-bind="value: A"/></div>
<div>B: <inputtype="text"data-bind="value: B"/></div>
<div>C: <inputtype="text"data-bind="value: C"/></div>
<div><inputtype="button"value="模擬資料載入"data-bind="click: load"/></div>
<divclass="disp">C = <spandata-bind="text: C"></span></div>
 
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"></script>
<script>
function myViewModel() {
var self = this;
            self.A = ko.observable("A");
            self.B = ko.observable("B");
            self.C = ko.observable();
            ko.computed(function () {
                self.C(self.A() + "-" + self.B());
            });
//假裝由DB取得資料更新至ViewModel
var dataFromDb = { B: "Y", A: "X", C: "Overwrited" };
            self.load = function () {
for (var p in dataFromDb)
                    self[p](dataFromDb[p]);
            };
        }
var vm = new myViewModel();
        ko.applyBindings(vm);
</script>
</body>
</html>

宣告A, B, C三個observable,用一個computed建立C = A + "-" + B的關聯,如此當A或B被修改,C會跟著連動,而C也可以直接被修改。另外,加入一個小函式模擬由資料庫取得物件,將其屬性一一寫入ViewModel。測試結果如同預期,莫非這樣就可以搞定收工? 線上展示

並沒有!!

搗亂的時間到了。在程式加入陷阱: 來自資料庫的內容,C為使用者另外輸入,不等於A、B相加,而更新時不保證A、B、C屬性的更新順序。換句話說,有可能更新A、B再更新C,也有可能更新C,再更新A、B。

//假裝由DB取得資料更新至ViewModel,但有個小問題,
//Server傳來的資料屬性順序是C、B、A
var dataFromDb = { C: "Overwrited", B: "Y", A: "X" };

動一點手腳,將dataFromDb屬性順序改為C、B、A。結果load函式會先更新C,再更新B、C,資料庫讀取到的C值("Overwrited")被罝換成"X - Y",與資料庫讀得內容不同,程式就壞了~ 線上展示

為克服這個問題,我們在ViewModel中增加載入旗標--loading,同時為了讓網頁能一併顯示
載入狀態,loading被宣告成ko.observable(false),方便<span data-bind="visible: loading">載入中</span>直接繫結。load()函式則加入邏輯,開始設定資料前將loading設為true,設定完畢後設為false,而computed則加入檢查,當loading()傳回true時,代表正將ViewModel覆寫為來自資料庫的內容,先停用C = A + "-" + B的運算。

function myViewModel() {
var self = this;
            self.A = ko.observable("A");
            self.B = ko.observable("B");
            self.C = ko.observable();
//增加loading旗標,以便在載入過程停用連動
            self.loading = ko.observable(false);
            ko.computed(function () {
if (self.loading()) return;
                self.C(self.A() + "-" + self.B());
            });
 
//假裝由DB取得資料更新至ViewModel,但有個小問題,
//Server傳來的資料屬性順序是C、B、A
var dataFromDb = { C: "Overwrited", B: "Y", A: "X" };
 
            self.load = function () {
                self.loading(true);
for (var p in dataFromDb)
                    self[p](dataFromDb[p]);
                self.loading(false);
            };
        }

程式範例如上,但其中有Bug,有人發現了嗎? 線上展示

按下【模擬資料載入】鈕後,C出現的是X - Y,不是Overwrited!! 原因出在computed為了判斷該不該更新C而讀取loading這個observable,於是computed訂閱了loading;當load()更新完資料執行self.loading(false),觸發原本要避免的C = A + "-" + B computed邏輯,一切白搭。

針對在computed裡取值卻又不想產生訂閱關係的情境,Knockout有個祕密武器 – peek()

            ko.computed(function () {
//改用peek(),避免loading變動時觸發重算
if (self.loading.peek()) return;
                self.C(self.A() + "-" + self.B());
            });

將self.loading()改成self.loading.peek(),重新試一次,這下總OK了吧? 但以上的寫法大有問題,有人猜到了嗎?

C終於出現Overwrited了耶!! 等等,載入資料後,C = A + "-" + B卻壞了?? 線上展示

這是Knockout新手常犯的錯誤之一,在官方文件 How dependency tracking works一節有段重要說明:

computed追蹤相依性的運作原理如下:

  1. Whenever you declare a computed observable, KO immediately invokes its evaluator function to get its initial value.
    當computed被宣告時,KO會立刻執行一次以取得初值。
  2. While your evaluator function is running, KO keeps a log of any observables (or computed observables) that your evaluator reads the value of.
    在執行過程中,KO會一一記下程式讀取哪些observable及computed
  3. When your evaluator is finished, KO sets up subscriptions to each of the observables (or computed observables) that you’ve touched. The subscription callback is set to cause your evaluator to run again, looping the whole process back to step 1 (disposing of any old subscriptions that no longer apply).
    執行完畢,KO會自動訂閱步驟2所記下的每一個observable或computed,只要其中任何一個有變動,整個computed就會再重新執行一次,重複步驟1,2並重新定義因讀取產生的訂閱關係,並抛棄不用的訂閱。
  4. KO notifies any subscribers about the new value of your computed observable.
    KO通知此computed的訂閱者新值。

值得注意的是,KO不只第一次會偵測computed對其他obervable、observableArray或computed的依賴關係,而是每次執行computed都要重新偵測、重新訂閱。因此,如果在computed中有if分支,某些條件下才會參考某個observable,便會出現因某次沒有執行到,便不再追蹤該observable的窘境。如同先前的實例,一旦if (self.loading.peek()) return;生效,後方的self.A(), self.B()沒被執行,整個computed就不再因A或B變動觸發重新執行。要解決很簡單,在if邏輯之前將A, B值先存為變數,就能避免因if邏輯差異影響訂閱範圍。程式只需簡單修改成:

            ko.computed(function () {
//computed每次執行都會重新計算訂閱關係,
//故要避免因if分支漏掉訂閱,這是初學者常犯的錯
//最簡單的做法是if分支前就讀取observable
var newValue = self.A() + "-" + self.B();
//改用peek(),避免loading變動時觸發重算
if (self.loading.peek()) return;
                self.C(newValue);
            });

終於,搞定收工! 線上展示

重點復習:

  1. computed每次執行都會重新偵測參考到的observable、observableArray及computed並建立訂閱,以便下次變動時觸發重算。
  2. computed中如有if分支,要確保所有要訂閱對象都要被讀取到,以免失去相依性。最簡單的做法是在if分支發生前讀取observable存成變數。
  3. 如在computed中只想參考某個observable,但不希望在其變動時觸發重算,可使用observable.peek()取值。

下課!!

[KO系列]

http://www.darkthread.net/kolab/labs/default.aspx?m=post

TechDays 2013隨堂筆記0924

$
0
0

TechDays 2013來了,又到了有為中年化身勤奮學員,努力聽課抄筆記的時刻。一如往例,隨堂筆記不求完整通順,旨在保留重要關鍵字備忘並供日後爬文研究,順手分享大家參考。

【Keynotes】

  • 在企業環境追求行動裝置二合一: 平板加鍵盤變筆電,差不多是Surface + Win8的概念。(個人看法: 輕量級電腦使用者有機會適用,但重度使用者[指用電腦幹粗活兒的黑手]仍得靠桌機維生)
  • 冒出一個Micro Love CIO - 宋少卿(大驚!),原來是MS Solution情境劇
  • Win8.1在企業的應用。亮點: 開始鍵(可以在桌面模式帶出應用程式清單,可排序或搜尋,但仍非傳統式資料夾結構)、一開機可以直入桌面模式、可重裝OS但保留個人檔案、BitLocker全磁碟加密(陳冠希也說讚) 、AppLocker(限制可安裝App清單)、為不同部門設定專屬開始畫面、平板電腦登錄取得企業內存取權限、圖片手勢密碼輸入、Assigned Access(某個帳號登入後只能使用某個App)
  • App Demo: 泡沫紅茶店POS、3D引擎模型、百貨公司樓管工具
  • Lync行動分機: 只要有Internet,分機可以跟著平板或手機四處遊移。所以,可以在長灘島接公司分機談公事哦~ (好可怕) Lync視訊會議支援Powerpoint分享、視訊對談。也可以與Skype IM互通交談。
  • Lync Client支援Android、iOS [看到小工具: Android Screen Monitor,在PC顯示Android畫面]
  • Office 365 / SkyDrive: 文件資料夾分享給特定對象、支援拖拉檔案上傳操作
  • Exchange Online線上服務: 信箱容量50GB、偵測文件有個資阻止寄出
  • 敏捷開發: 情境,委外團隊的Source Control。雲端TFS,支援需求管理、Iteration面板、Intellitrace記錄正式環境的錯誤細節、將程式執行歷程圖形化顯示(CodeMap/資料流處理視訊)、BrowserLink(在瀏覽器上點選元素,自動捲到對應的ASP.NET端元素)、一次叫出各種瀏覽器進行檢視、Team Foundation Service(雲端版可產生20萬個Client的壓力測試,Preview階段免費!)
  • 維運管理: Scenario - 大量機器,實體機器虛擬機器交雜、Windows/Linux/NET/Java/SQL/Oracle、Azure。產品 – System Cener Operations Manager,SCOM。
  • Windows Azure – 快速增加機器、Autoscaling(依流量自動增減機器數量)、Service Management Portal(Azure Pack)用Azure相同介面管理公司私有雲
  • BI - 支援HDFS、從FB粉絲團取得文章、按讚次數、讀者性別資料進行分析
  • SQL Server 2014記憶體資料庫、AMR Tool(分析哪些資料適合放入記憶體)

Modern ALM by Visual Studio & TFS 2013 by Franma

  • 推行軟體工程工具常見難題: 管理者與開發者的觀點往往對立,要取得二者共識才會真心接納
  • 要訣: 減少對開發者工作方法的改變(否則上有政策,下有對策)
  • 組織愈大,衝突愈多,但小團隊有小團隊的痛。團隊無論大小都需要流程,惟掌控重點不同
  • Plan(Project Management/Dashboard on TFS) –> Develop/Test(Load Test by TFS & Azure) –> Release(InRelease自動化部署) –> Operate(SCOM)
  • 微軟正在縮小Delivery Cycle: 改版頻率 TFS2008 10-12wks, TFS2010 6wks, TFS2012 3wks
  • 開發者認領工作,成為進行中的工作,簽入的程式碼再與其相關
  • TFS提供Web化指標Dashboard(Metro Style)及管理介面
  • 待處理項目: 功能(Function)、使用者劇本(User Story)。功能或劇本可以再加上Tag(標記),查詢時可使用Tag作為條件。
  • 工作階段: 新增、作用中、已解決、已關閉,每個功能或劇本都會歸屬於某個階段,各階段可設定WIP限制(Work In Process,例如: 測試團隊同一時間只能承載三項工作)
  • 當工作量一大就卡在特定人員身上 --> 用WIP限制來管控,避免某人操爆、其他人Idle,總體效能下降
  • 一個Team Project可以有多個燃盡圖嗎? --> 同一個Project分成兩個Team,可針對不同Team進行分析
  • 管理權限下放給相關負責人,Souce Control也可做細部設定
  • 自動產生報表,避免Paper Work侵蝕工時
  • 導入工具最大的管理意義: 讓問題透明化,被突顯出來。(如果有必要做內外帳也情有可原,但是別省略內帳,才不會失去工具的精神)
  • 同一個Team Explorer可以並存TFS SCC Provider及GIT SCC Provider,即使用GIT做版控,仍可搭配TFS的其他管理功能。
  • 聊天室功能 <== 可將原本散在LINE、Skype中的討論納入正式記錄
  • 用Web介面為程式碼中加入具名註解,其他人可回覆該筆註解進行討論
  • CodeMap偵錯工具,視覺化呈現Callstack+執行歷程,可針對線上環境蒐集的Intellisense Log繪製圖表。
  • Code Lens - 程式碼關聯,直接顯示變更集、工作項目、測試記錄
  • Alt-F12 – 另開視窗看Go Definition的程式碼,避免跳來跳去迷失方向
  • Memory Dump Analyzer - 找出Memory Leak瓶頸 (Process Dumper產生傾印檔,直接用VS開啟檢視)
  • 手動測試: 提供Web版的Test項目,可在網頁勾選成功失敗,貼圖填寫產生Bug單
  • Test Manager: 記錄操作過程,側錄動作及區部圖塊,附在Bug單方便其他人了解或重現錯誤。
  • InRelease: 自動化建置,提供OP Team過版上線的專屬工具

Visual Studo測試功能介紹 by 王寧疆

  • 測試目的: 提高客戶滿意度、單元測試有助於早期發現降低修正成本、提高開發速度、與Refactoring結合
  • 黑箱測試: (假設不了解程式碼寫法)
    自動程式碼UI測試、MS Test Manager
  • 白箱測試: 正確/不正確輸入都要測到
    單元測試/程式碼涵蓋範圍分析、程式碼度量
  • Demo: 建立測試專案,在[TestMethod]上按右鍵執行測試,由燈號檢視結果(綠-成功、紅-失敗、黃-忽略)
  • 常用Attribute:
    TestClass、TestMethod、Ignore、ExpectedException(指定例外型別,沒例外反而算測試失敗)、AssemblyInitialize/AsseblyCleanup、ClassInitialize/ClassCleanup、TestIntitialize/TestCleanup、Timeout(1000)
  • 涵蓋度報告(Team Explorer在測試上按右鍵產生): 確保該測的都有測到,但是涵蓋100%並不保證沒問題,例如: 未考慮分母為0。涵蓋度報告可在程式碼加底色方便看出哪些有被跑到、哪些沒有。
  • Cyclomatic complexity – 循環複雜度(條件式產生的複雜度),兩個if then else = 2 * 2 = 循環複雜度為4,透過程式碼度量可檢測。
  • VS2013新功能: Support Win Store App單元測試、自動組建時進行單元測試、CodeLens(直接在Method上標示被引用狀況、測試記錄、作者、變更集)
  • 單元測試進階:
    • 已排序測試: 一旦失敗,後面的測試就不用做
    • 偵測測試: 一邊測試一邊Line by Line Debug
    • 剖析測試: 一邊測試一分析CPU、Memory使用率
  • 【插花】大會堂的冷氣溫度恰到好處,座位軟硬適中,加上爾有鼾聲突擊,是周公所安排,最險峻的BOSS級試鍊場!
  • 自動程式UI測試: 限XAML WinStore App(可錄下手勢、觸控位置,但不支援WinJS)、網頁測試限IE11
    新增Coded UI Test Project,錄製操作過程可以拉出瞄準器,顯示屬性視窗,加上檢測條件(Very Very Cool!!)
  • 資料驅動測試: 由外界帶大量測試資料,減少測試的痛苦指數,用DataSource Attribute。
    Demo 將資料放在Excel -> [DataSource("System.Data.OldeDb", @"Provider….")]
    TextContext.DataRow["Col1"]
  • FakeFramework – 克服測試需要外部資源(File、DB、SPS...)、未完成功能才能進行的困難(since VS2012,好物!!)
    Shim組件(取代.NET組件)、Stub組件(未完成組件)、Stub服務、Stub資料庫
    例如: 你怎麼測DateTime.Now等於某一天? 土法鍊鋼: 改系統時間,測完再改回來
    在測試專案中【加入Fakes組件】加入System.4.0.0.0.Fakes
    using (ShimsContext.Create()) { ShimDateTime.NowGet = () => new DateTime(2013,9,26); TestBlah(); }
  • 程式碼分析: Microsoft安全性規則 -> 可偵測SQL Injection漏洞,報表中的弱點項目可直接轉為WorkItem
  • 分析傾印檔案: 先中斷,執行"偵錯/另存傾印檔",比較兩次Dump檔,找出二者記憶體用量、物件個數的變化。Demo範例,一個物件註冊EventHandler卻未釋放導致MemoryLeak
  • Azure雲端: 從雲端進行壓力測試,依使用量付費,可模擬極其壯觀的情境!!

F2E for Enterprise by 啊嗚 josephj

  • 為什麼企業需要Front End Engineer(F2E)?
  • Joseph F2E工程師資歷8年 Yahoo 4yr, D-Link 4yr, Faria Systems since 2013.6 SaaS
    (編註: 除了熱心於F2E傳教,Joseph的另一項驚人事蹟是跑步環島,他的熱血量至少有一萬加崙吧!)
  • 前端的範圍: HTML、CSS、Javascript、安全、效能、跨瀏覽器...
  • 傳統網站開發: Boss-Driven-Development,老闆提出網站構想,單兵開發者把全部東西生出來(DB+Server+Service+Client,"全端工程師"來著)
  • 1998網頁標準開始形成~~ HTML、CSS、JavaScript分工明確,HTML Tag不應摻雜style="…" onclick="…"
  • 2001 Yahoo設立F2E職位 2004 台灣雅虎出現第一位F2E
  • 視覺設計 只產出PSD、後端工程 只管Model、Server端的程式
    HTML、JavaScript、CSS、網頁效能、跨瀏覽器全部交給前端工程
    把PSD轉成網頁,看起來一模一樣,但背後血淚斑斑
  • 2005 Yahoo F2E 2人->600人,足證F2E趨勢
  • 2007 HK Asia F2E Training
  • 前端成為顯學: 各種Framework、程式庫、中介語言輩出: TDD 測試框架Jasmine、HAML、CoffeeScript...、前端社群如雨後春筍出現、JSDC.tw 2012(報到率超過9成)
  • 企業的前端需求: 像FB一樣的互動性、支援iPhone/Android… 未來將成基本要求
  • 前端對企業的重要助益: Services to Devices。跨瀏覽器是前端的基本功! 用網頁技術開發App,節省開發者寶貴的青春,但有其限制。
    * PhoneGap提供存取Device API
    * titanium 用JS寫,Build成Native Binary
    * Firefox OS,OS本身就是瀏覽器,少掉多了中間層損失的效能
  • 2004 Mainframe幾乎消失,2012 類似Mainframe概念的SaaS再度掘起。例如: 雲端會計系統,每個月花$29(約台幣1000),公司就有會計系統可用,甚至連會計師都不用請。(國內因法規限制較不易實現)
  • Faria Systems自己也用別人的SaaS: Highrise, Basecamp, HipChat, CODE Climate, github, New Relic, logentries(使用者錯誤回報蒐集), AirBrake, CODESHIP。雖然企業內部也可自行開發,但不符經濟效率,且品質功能必有差距,必要時還是該閃開讓專業的來。
  • Xero 美麗的線上會計系統  Close.io 新潮的CRM介面
  • 前端工程帶來的改變:
    * 工程師喜歡邏輯、資料,不在意UI
    * 設計師喜歡呈現,不在意方法(不會程式)
    F2E能完美結合二者,形成更好的分工,讓員工專注在自己喜愛的工作上,不要逼搞資料的工程師去學會UI。
  • Bootstrap -- 找不到前端的強者,也能刻出足堪入目的UI
  • 前端絕非"比較簡單",開發偵錯工具較不成熟、環境變化較大、要考慮效能/安全,而技術翻新的速度非常快... 代誌不像憨人想得哈尼甘單~
  • http://f2eclass.com前端課程分享(稍舊,2005)

TechDays 2013隨堂筆記0925

$
0
0

DBA的十大惡夢 by 楊志強

  • 資料庫出現I/O問題
    • DBCC CHECKDB可以發現故障,但結果文字閱讀不易,INSERT INTO @T EXECUTE('DBCC CHECKDB WITH TABLEREULTS'),將SP執行結果塞入Table進一步利用(甚至發出電子郵件通知)
    • 事前防範Storage故障所引發的問題
  • 埋藏在事件檢視器的潛在問題
    • T-SQL無法直接讀取系統事件
    • Powershell Get-WmiObject –Class Win32_NTLogEvent
    • Get-WmiObject –Query "SELECT * FROM Win32_NTLogEvent WHERE LogFile='Application' AND EventID=1" …
    • 利用PowerShell Script將事件抓進DB進一步分析、應用
  • 亡羊補牢,解析交易記錄檔
    • 以下為Undocumented SP: 不保證永久有效
    • SELECT * FROM fn_dblog(null, null)
    • 由fn_dblog找出Transaction_ID,組成LSN可RESTORE LOG ... FROM DISK=... WITH RECOVERY, STOPBEFOREMARK='LSN:.....' 將資料庫還原到指定時間點
    • 此法不可靠也不夠直覺,良心話: 花錢買3rd Party工具才是王道
    • fn_dump_dblog... --> 離線Log適用
  • 快速重建交易記錄檔
    • 搭配核心數、HD數開多個DataFile可以增進效能,但Log檔一個就好,開多個適得其反
    • Log檔毁損的緊急處置方法(Emergency Mode->Single User Mode->DBCC CHECKDB自動補Log檔)
  • 空中換引擎,解救爆滿的HD
    • DBCC SHOWFILESTATS
    • DBCC SHRINKFILE('OrdHist2',EMPTYFILE) 縮小資料庫,將特定DataFile的資料移走,過程中DB可如常讀取寫入,速度不快但系統持續可用
    • 在Filegroup加入新的DataFile,利用上述指令將資料移到新增的DataFile
  • 正式資料庫被移除
    • DDL Trigger,遇到砍DB指令時Rollback並丟出錯誤,防止資料表被移除
    • 用RaiseError with Log,將警示寫入事件檢視器
  • 偵測SQL Agent停止
    • T-SQL SELECT * FROM sysprocesses檢查SQL Agent是否存在
    • 設成Scheduled Task定期檢查
  • 使用手機查看過去15min的CPU
    • sys.dm_os_sys_info取得CPU Load,串URL將資料塞入Google Chart
  • CPU飆高時自動啟動追蹤功能
    • SQL Profiler可設停止時間,無法預定時間啟動,但Profiler可以將設定存成SQL Script,執行之即可啟動Trace
    • fn_trace_gettable()抓出Trace內容以Table方式呈現
  • 用手機KILL鎖定問題
    App/IVR(語音操作)
  • Super SQL Server FB社團 (本場次的所有內容會放在該社團)

Visual Studio 2013 Keynote

  • VS產品更新頻率加快,約每季更新一次
  • VS2013 Title Bar增加笑臉/哭臉鈕,對各功能進行回饋(按讚或嚧爆?)
  • Medern Web現代化網站的要件
    • Responsive Design - 適應各裝置各解析度
    • Web API/Open Data – 服務導向
    • Social Integrated – 與FB整合
    • Scalable – 自動Scale up、Scale out
  • 可將設定儲存到雲端,在多台機器間同步VS設定
  • ASP.NET專案改為單一專案範本,建立精靈時決定要用MVC、WebForm、SPA、WebAPI,且支援自訂選取個別為專案加入WebForms/MVC/WebAPI功能
  • ASP.NET專案內含Bootstrap,Responsive Web Design Ready
  • BrowserLink,一次開啟多種瀏覽器(IE、Chrome、FF...),改好ASPX存檔,按Rerfresh(CSS不用按Refresh就更新),所有瀏覽器自動更新到修改後的結果
  • app.Use***Authentication,輕鬆支援Microsoft Account、Google Account、FB Account認證整合
  • Azure部署程序更簡化
  • *** 我是Win8.1相關功能分隔線 ***
  • Win8/8.1/WP8 Unity3D 模組免費(iOS/Android要1500元)
  • Win8.1商業平板,擺脫平板只是for fun的刻板印象
    零售業: 店員平台,放入商品,設定折扣,刷卡結帳
    醫院: 病人資訊、看診輔助
  • Geofense(地理圍欄),進出某個地理空間時啟動特定App
  • 支援3D列印API
  • App評價不佳的原因: App Hang、閃退、反應時間太長、過度耗電
  • VS2013支援WinApp效能診斷(CPU Load取樣、XAML UI回應性、耗電)
    記錄一段時間的CPU、網路使用狀況,估算電力耗盡時間、JavaScript函式計時、JavaScript記憶體
  • *** 我是跨平台開發(Xamarin)分隔線 ***
  • Xamarin為付費商業軟體
  • Android可直接拉控制項,馬上寫C#;iPhone要先在Xecode拉好框架,再到VS裡寫Code Behind,下個版本會支援直接拖拉
  • iOS專案仍需要Mac機器進行編譯
  • 跨平台開發的痛: 要養不同的Team、技術、元件難以共享。Xamarin可將Business Logic抽出寫成Core Library供iOS/Android介面共享。(簡便方法是用Add as Link大絕,不然用Portable Library亦可)
  • 支援.NET 2.0 SOAP Web Service、WCF、RESTFul
  • *** 我是Cloud部署與測試分隔線 ***
  • Azure已納入Oracle DB/Linux VM、PHP... 愈來愈開放
  • Azure端已有VS2013 RC桌面環境可登入測試。(MSDN訂閱可啟用Azure試用)
  • 巨型壓力測試: 模擬數萬個Client,萬箭齊發情境
  • 錄製測試 -> 轉成壓力測試 –> 選擇用Team Foundaton Service跑雲端壓測(需幾分鐘等待設定完成) -> 由雲端取回測試報表
  • 試用版上限: 15000人次-分鐘。ex: 5000人次可以跑三分鐘
  • MSDN訂戶Azure權益,Ultimate版本每月有4600元額度,免提供信用卡資訊,9/30前開通可以抽跑車
  • *** 我是TFS分隔線 ***
  • TFS(Server) vs TFS(Service),Server才能客製流程跟工作表單、BI分析報表,支援AD登入,但Service改版速度很快,或許不久後就會改觀
  • 功能分解成使用者劇本,使用者劇本再分解成工作。Project內包含多個團隊,成員不同,有各自的Portal及Burndown
  • InRelease –> GUI部署工具,設定DEV到UAT、UAT到PROD的設定,One-Click完成,還可以設定Approver(出包誰負責,在江湖打滾過就知道這個功能多重要 XD )
  • *** 客戶證言: 趨勢科技Anden 分享使用TFS敏捷開發的經驗
  • VS2013 9/9 RC、10/18 RTM <== 有沒有VS2012還沒用熟,新版就出來的八卦

運用Scrum進行專案規劃– Agile Planning by Ruddy

  • 每回見到Ruddy老師都會燃起"我可以再Coding十年"的信心,老師儼然已成中年程序員的精神寄託兼信仰中心
  • 敏捷 - 利用短週期的反覆開發獲得頻繁回饋
  • Wiki上的Scrum要素是鬼扯淡
  • Planning –> 開發 Daily Standup Meeting –> Demo(交付) –> Retrospective 回顧
  • 為什麼要Agile? 老闆: 每週要交付一個版本,通過測試,而且要讓客戶點頭 --> 不可能的任務
  • 達成不可能任務的方法: 1) 將大問題拆成小問題(用短週期換取客戶回饋) 2) 只專注於最重要的事件心無雜念 3) 確保交付的東西可以運作 4) 一定要尋求回饋 5) 必要時可以改變流程  6) 勇於負責(有錯就立刻改) 要設期望值/控制品質/控制進度/把自己當客戶去思考產品
  • 開發軟體是世界上最崇高的工作 -- 用軟體幫助世人,增進人類全體之生活來著
  • 三個簡單準則: 1) 初期不可能蒐集完整需求 2) 需求一定會變化 3) 總會有任務超時超支
  • "可工作的軟體"是衡量專成功的主要度量指標
  • 經驗的神奇之處在於提醒你不要再犯同樣的錯誤。第N+1次Iteration要以第N次Iteration的回饋為依據
  • Scrum歷史
    竹內弘高/野中郁次郎 哈佛論文 豐田式管理 首次出現Scrum一詞,Ken Schwaber(在MS)&Jeff Sutherland以Scrum為主題寫了Paper,在鹽湖城會議發表敏捷宣言
    Kent Beck 程式有壞味道
    Martin Fowler 倡議重構(Refactoring)
    Mike Cohn - User Story的發明人
    Henrik Kniberg – 著作"煙硝中的Scrum",最暢銷的一本(Free電子書)
  • 把大案子大需求拆解細分個個擊破 -> Scrum很適合做大案子
  • Timebox –> 時間箱,時間到就要有東西出來。不是時間箱的例子: 新創公司的作品,寫到好為止。
  • Prodcut Backlog –> Sprint Backlog –> Sprint(1-4wks) [每天Standup Meeting,焦點集中在: 昨天做了什麼? 今天要做什麼? 有什麼困難? 勿超過15分鐘]-> Retrospective
  • 浮現式設計(Emergent Design) – OOP、Design Pattern
  • 完成的定義: 由團隊認定,多半由PM或QA決定
  • 三種角色: Project Owner、Scrum Master、Team
  • Artifacts(工作物件): Product Backlog、Sprint Backlog、Burndown Chart
  • **啟動計劃**: 10問
  • (1) 問尖銳的問題!! 事後才問是事後諸葛無意義,找到正確的人問對問題,會有驚人回饋
  • Remind ourselves why we are here? 專案的目的? 客戶是誰? 為何要先做這個專案?
  • 好客戶: 主動提供意見。一般客戶多半要先看Demo,經詢問才會出聲
  • 目的: 更好更明確的抉擇、平衡矛盾共加以取捨、充分授權獨立思考才能更好
  • 加班要加對時候: 開始、結束、中間點放,長期加班會造成生產力下降
  • (2) Create an elvevator pitch! 30秒電梯演講! 30秒和二句話描述這個專案
    目標客戶、需求機會、產品名稱、產品類別、主要優勢、競爭對手、主要區別
  • (3) 設計產品包裝
    一目了然、引人注目、提出好處
  • (4) 列出否定清單
    否定項目區分成需要清除的巨石、無須擔心的東西
  • (5) 認識鄰居
    設備維護部門、MS Support、DBA、Open Source的Community是否活躍… 判定可用資源及其可靠性
  • (6) 展示解決方案
    架構巨觀藍圖,確保溝通有效,獲取其他單位的意見
  • (7) 關注那些使我們睡不著覺的問題
    找出風險,在尚未萌芽時解決
  • (8) 估算項目的規模
    無法準確評估,但仍需要讓需求者知道何時可以拿到成果,即使只是猜測也好
  • (9) 明確知道我們要捨棄什麼?
    範圍、預算、時間、質量(不能妥協)、簡單好用、簡單又簡單、詳細設計 <== 決定優先權重
  • (10) 對專案所需付出資源加以展示
    專案需要的時間、花費、何種團隊才能勝任
    EX: 7人三個半月120萬,首次Release - 小於3個月完成建置、一週驗收測試、一週培訓、交付
  • 最有效的Feedback –> 匿名回饋是最真實的
  • 敏捷不是互相猜測,而是馬上做決策。讓共同的目標,讓團隊自我管理,主動成長。

Visual Studio 2013 新功能及訣竅 by demo

  • VS2013小冊子裡有詳細新功能介紹
  • VS2013的Title Bar右上角有現在登入的使用者,可在雲端建立共用Profile,建議開發設定選C#(熱鍵為單手操作,效率高),共用Profile可使多台機器的設定保持一致(同步範圍可調: 外觀、環境別名、快速鍵、啟動、文字編輯器)
  • 選項設定畫面可輸入關鍵字在茫茫選項大海中篩選出要找的項目
  • TFS Explorer改為Metro化,更易操作
  • Code Lens
    • 在方法上顯示被幾個地方呼叫(n 參考),點下後顯示參考來源清單,且以非同步方式載入,不會卡住操作(比類似外掛更先進)
    • 如果有單位測試,可顯示測試次數
    • 專案如果連結到TFS,可以顯示作者及變更次數
    • 支援熱鍵!
  • View Definition vs Go Definition
    • 傳統按F12一路追蹤穿過多層後,開發人員常會迷失所在位置
    • Alt-F12 View Definition,另開浮動視窗顯示定義來源,原有視窗不動
    • 熱鍵不用背,Mouse OnHover就會有提示
  • 捲軸加強:
    • 捲軸色塊顯示: 區別異動未簽入、中斷點、異更後未儲存...等等
    • 垂直捲軸可以"文件地圖"方式顯示(展示整份文件縮圖)
    • 滑鼠經過捲軸時用浮動視窗預覽該位置的程式碼
  • Tips: 同一個檔案可切成上下兩塊檢視區(VS2012已有),同時參照不同段落
  • 巡覽至(Ctrl + ,) 快速在程式碼中找到吻合關鍵字的程式
  • Alt + 上下方向鍵,將某行程式向上或向下搬移
  • 打字時預設會自動補上右括弧、右引號,若不習慣可關閉
  • 通知視窗: Title Bar旗子圖示,通知有套件可更新
  • BrowserLink儀表板:
    能主動偵測哪些Browser開啟了專案(方法: 在網頁偷插入JS當內應)
    ASPX或CSS更改時,Browser們也會自動顯示最新結果
  • SQL資料比對工具: VS2012消失,只剩Schema Compare,VS2013又加回來了
    工具/SQL Server描述比較(比對兩個DB Schema的差異),方便將Schema更新部署到目標DB去
  • 工作清單: (檢視/工作清單)
    類似內藏在VS內部的ToDo List,不需要離開IDE就可用的便利貼
    //TODO //HACK(鋸箭) //UNDONE –> 自動列入工作清單的註解區
    捷徑 Ctrl + E,T, Ctrl + K,H,按下後左方出現箭頭小Icon,自動加入工作清單
  • 加入/移除書籤: Ctrl + B,T, Ctrl + K,K,左方出現書籤Icon
    Ctrl + B,P(上個書籤) Ctrl + B,N(下個書籤)
    Ctrl + B,C(清光書籤)
  • Ctrl + Shift + V: 循環剪胋環 (找回前幾次複製的內容)
  • Ctrl + F5 Attach to Process比重新Build重跑IIS Express快,可取代F5使用
  • 中斷點視窗: 一次列出所有中斷點方便管理
  • 中斷點條件式、計次: 指定特定條件成立時才中斷,或執行過幾次後才中斷,取代猛按F5。
  • 【插花】如果有冷氣廠商要強打恆溫舒眠功能,可以考慮用"讓你的客廳像TICC大會堂一樣舒適"當廣告詞,這溫度拿捏太到位了,好多人被周公擄走,超可怕。
  • 整行複製、剪下: 不要選取任何字按Ctrl + C
  • 列編輯: 按Alt加上下箭頭,可垂直整排插入及刪除
  • Ctrl + Enter / Ctrl + Shift + Enter : 取代按Home+Enter
  • 主字型推薦Consolas,等寬,且1l0Oo區別明顯;編輯器工具提示改為細明體,粗體比較明顯
  • 套件介紹: 網站 http://vs.demo.tc

Visual Studio跨平台開發 Xamarin by Terry

  • Mono Project, 可以在Linux執行C#。
  • Xamarin – 為iOS SDK、Android SDK寫出一對一對應的.NET Library
    在iOS透過AOT編譯成ARM Binary(iOS時無法使用Refelcdtion)、在Android透過JIT編譯成ARM
  • iOS的編譯只能在Mac上進行,TFS無法整合Build程序,但Android可以
  • Xamarin: Objective-C跟Java可以做到的,都可用C#實現,與Apple同一天推出iOS SDK對應版本,更新很即時。iOS 7 Ready!
  • LINQ、Lambda、XDocument、Parallel LINQ… .NET 4.5 await/async都支援
  • VS+Xamarin可通吃500M部iOS, 600M Android, 16M Mac, 1.5B Windows
  • 按F5就可Compile/Debug,與VS的開發體驗一致
  • iOS事件範例
    btnHello.TouchUpInside += (sender, e) = { this.lblMsg.Text = "Blah"; };
    imgBoo.Image = UIImage.FromFile("blah.png");
  • 資料存取
    本機: XML、SQLite 遠端: Oracle、SQL、SQLite
  • SQL Cipher 付費元件,全資料庫加密AES256,且對效能影響不大
    new Mono.Data.Sqlcipher.SqliteConnection(),其餘用起來跟.NET SqlConnection()一樣
    如果不花錢,則可以自己在寫入DB前加密,取出時解密
  • Demo: 呼叫Web Service
  • 付費圖表元件: Bubble、Radar、Pie、Candle(K線)、Tower、Pie...
  • 程式碼共享: Add as Link + #if/#endif(條件式編譯),讓Android、iOS App共享程式
  • 實例示範: YouTube搜尋,顯示影片,RT + Windows Phone + iOS + Android 四種平台
  • Youtube.Core.YoutubeSearcher.cs用Add as Link,供Youtube.iOS, Youtube.Android… 使用,各專案定義不同的條件化編譯宣告
  • Youtube查詢API: gdata.youtube.com/feeds/api/videos?q=..
  • Android專案有工具箱可以拉控件配置頁面,iOS不支援(要用Xcode拉)
  • 雖然是用C#,不同SDK有不同的Method,GetCell(iOS) vs GetView(Android),開發者仍得學習不同平台的SDK,切換開發時難免打架
  • 跟PhoneGap比較: PhoneGap非原生,效能輸一截,適合純UI呈現且效能不是重點的情境
  • Source Code已在github
  • 資源: http://aka.ms/vs-xamarin

TechDays 2013隨堂筆記0926

$
0
0

ASP.NET MVC5新功能探索 by 保哥

  • 註: 簡報檔有更完整內容
  • One ASP.NET概念
    以ASP.NET為基底的應用區分成Sites及Services兩大塊
    Sites: MVC、Web Pages、Web Form、SPA
    Services: Web API、SignalR
  • VS2013 ASP.NET專案範本只留一個(但仍保留VS2012的範本可選),新增專案過程再選WebForm、MVC、Web API、SPA、Facebook(包含建立範例檔案),另外可勾選WebForm、MVC、Web API,會再建立相關資料夾並參照核心程式庫
  • Web API說明頁面
    Areas/HelpPage下有個完整的MVC模組,能自動產生Web API說明文件(以XML Documentation為基礎,列出各Action[含POST、PUT、DELETE不同動作對應]的說明)
    要產生文件: Areas/HelpPage/App_Start/HelpPageConfig.cs 移除註解,啟用SetDocumentationProvider指定XML位置,便能自動依註解產生文件
  • Browser Link工具列
    VS2013透過SingnalR跟已開啟瀏覽器(不限IE,Chrome/Firefox亦可)溝通,實現VS2013控制瀏覽器動作(重新整理)、或是反過來由瀏覽器控制VS2013(在IDE將焦點移到特定元素)
  • Browser Link儀表板
    Inspect Mode: 在瀏覽器選取元素,VS2013也捲到該View對應的位置 
    Design Mode: 在瀏覽器中直接修改<div>的文字,自動改變cshtml
  • CSS修改時,網頁不需要Reload,只重新載入CSS,更新顯示更有效率
  • Web Essential 2013的額外強化
    Unused CSS: 瀏覽頁面過程記錄用到那些CSS,一路操作下來可以發現沒被用到的CSS
  • 靜態網頁預設不經過ASP.NET機制,故無法在網頁嵌入Browser Link用的SingnalR JavaScript
    修改config: <system.webServer><modules runAllManagedModulesForAllRequests="true" />…
  • Scaffold(中文被翻譯成支架,我個人比較喜歡鷹架 XD): 不能直接新增Controller、View,改由右鍵選單新增Scaffold加入 (沒有以前方便)
  • Bootstrap -- Twitter發展的網頁設計框架:
    Grid System 網格系統/排版
    Base CSS (Typography字型、Button Style按鈕樣式)
    Responsive Design 依解析度不同自動調適排版
    Icon by Glyphicons 圖示庫
    一些jQuery Plugin
  • ASP.NET內含Bootstrap為2.3.1,目前Bootstrap 3.0已推出(IE8+,IE7呷賽)
  • http://bootswatch.com/2<== 版型庫,也有精美的收費版型,17塊美金,很值得。下載bootstrap.css回來覆寫就可以更換
  • Bootstrap v3修改幅度很大,換版是大工程,Class Name大改,基本上新舊版不相容。
    http://upgrade-bootstrap.bootply.com提供線上將V2.* Class Name置換成v3對應名稱功能,但只涵蓋主要變異,還有很多眉角待自理
  • ASP.NET Identity System –> 取代ASP.NET Membership
    全新會員機制,統合所有ASP.NET專案(MVC、WebForm...)。EF Code First,新增欄位容易
    結合外部驗證: FB、Twitter、Google Account、Microsoft Account、Office 365。Claims-based authentication(ADFS)
    有多種後端可自由選擇: 只需符合IIdentityStore/DbConext即可,MySQ也OK
  • Identity核心類別
    * IdentityStoreManager: 儲存
    * IdentityAuthenticationManager: 登入/登出
    NuGet Microsoft.Aspnet.Identity.Core & Microsoft.Aspnet.Identity.EntityFramework
  • 四種驗證: 不驗證、個別使用者帳號、組織帳號、Windows驗證
  • 整合FB認證: 在FB申請開發帳號,取得AppId、AppSecret。因FB只接受SSL URL,記得要啟用ASP.NET專案的SSL
  • 整合Twitter驗證: 需要對外有效的URL
  • 擴充使用者欄位Models\IdentityModels.cs ApplicationUser、Models\AccountViewModels.cs、Controller跟View也要修改、進行DB Migration更新資料庫(增加欄位)
  • Action加上[Authorize(Roles = "MyRole")]限定角色存取
  • Authentication Filters
    ActionFilterAttribute/IAuthenticationFiler (OnAuthentication/OnAuthenticationChallenge)
    特定Action、特定Controller套用不用的驗證方式
  • Filter overrides
    MVC4支援全站套用特定Filter,MVC5加入[OverrideActionFilters]、[OverrideAuthentication]… 可覆寫全域設定(目前仍有Bug)

Enterprise Library 6 by 董大偉

  • 講師從包包拿出一堆東西跟全新手機引發騷動,莫非等下要問問題送手機... 原來是背包太滿
  • 彼得理論: 每個人最後都會被升職到自己無法勝任的職位。成為開發單位主管,對架構設計要有所了解。
  • 瞻之在前、忽焉在後: 趨勢難以掌握,學習沒有止境
  • 何時需要程式架構設計? 當參與開發人員變多、應用程式使用範圍變廣
  • 依民調: 程式可重用性常低於10%,總是在重寫(維護還不如重寫)。當架構夠好,即使技術平台改變,仍有機會重用(例如: Business Logic Kernel)
  • 重寫->因為當初沒有想好? 或根本沒想...
  • 架構設計多半應用在大系統: 因為重寫成本較高,如果應用程式要用超過2年就該考慮。可拋式的App,如有需要重寫無妨,倒不用多想
  • 範例: 抽出方法 GetDataTable(SqlCommand) –> 獨立成Class –> 獨立成專案
    掛上Namespace、連線字串改由設定機制讀取
  • 把直覺化的程式寫法抽出來改成獨立專案,需投入額外開發成本--目的在追求重用性、一致性、便於維護、減少重覆程式碼...
  • 持續重構,便會開始面臨抉擇,邏輯怎麼分配、介面間如何溝通... 多依賴前人的經驗,省去摸索走冤枉路的風險,這就是Design Pattern的價值
  • 架構設計是一張地圖、一套規範、一份指引、經驗累積
    (相信"我們得這麼做"很重要! 只是常常"我們需要這麼做,但不是這個案子,這次太趕了" XD)
  • 架構設計師: 充分了解使用者需求(要涵蓋所有的情境,不同的利害關係人),成為商業需求及技術需求間的橋樑,設法透過軟體滿足需求,並使架構具備足夠靈活度
    設計重點要突顯結構,隱藏細節,勿聚焦於程式怎麼寫
  • 經典: MPP電子書,參考資源 (請參見投影片,連結未來再補上)
  • 關鍵準則: SoC、Single Responsibility、Least Knowledge、DRY、Minimize Upfront Design(只設計當下需要的設計,不要過早做出大型設計)
  • N-Tier: Presentation Tier/Appication Tier/Data Tier
    Logical Layered Design –> 以部署為考量,Service Layer(對外服務介面)、Cross Cuttings Layer(所有Layer都有用到,例如安全),有需要的話才將不同Layer丟進不同Tier,放在同一Tier效能較佳
  • Service不該包含Business Logic,應該只用來將Logic提供外部程式呼叫(可以穿防火牆)
  • Business Logic Layer事關核心邏輯,很需要測試,切成獨立Layer可測試性會變高。
  • "當你能用程式測程式,軟體才有品質可言" 人工測試不可靠
  • TDD 先寫測試再寫程式,明明多花時間寫測試,開發時間反而會變短,真的!
  • MVC Pattern的重要性已被接受,最大的優點: 強迫新手也會分層
  • MVVM - 讓設計人員跟開發人員合作愉快
  • DDD – Domain Driven Design 開發時以Domain為主體的設計概念,專注在於系統要解決的核心問題。
  • CQRS – Command Query Responsibility Segregation: Query跟執行更新(Command)走兩條不同的途徑(Command交給Background Worker寫入Data Store,再以Event同步到View Data) Azure採行了這種設計概念 <== 編註: 這個概念挺棒,可拿來解決手邊某些專案所面臨的難題
  • Enterprise Library 6.0
    Reusable software component,實現設計模式,有多個Application Block,但不是每個都適用。
    PS: Enterprise Library的Hands-On Lab完整度相當高,不要錯過
  • "能設計的就不要寫死,能外掛的就不要內含"
  • Unity –> 可抽換的模塊,將邏輯丟進DLL,利用Unity輕易抽換
    在config中定義Container,定義某個Interace對應到哪一個型別,呼叫時選擇Container,透過.Resolve<IBoo>就能取得指定類別進行計算
  • Logging Application Block - 單一記錄方式,透過設定指定記錄寫入對象(File、事件、Mail)。提供非常Friendly的GUI設定畫面。
  • Exception Handling Blcok – 不同的Exception用不同的處置方式,有些要通知,有些要隱藏
  • 看不完的資源...
  • David老師今天無雙全開,程式熱血值爆表,大秀江湖傳說的"再給我五分鐘"連發技,不知有沒有打破TechDays的超時記錄,呵!

ASP.NET Web API v2 by Bruce

  • One ASP.NET的概念
  • Service for different devices(平板、手機、XBox...)
  • Web API,比Web Service、WCF輕量化,ASP.NET Web API 2隨VS2013發行,可由NuGet取得,限定.NET 4.5+ (登楞... 望向公司那一票Windows Server 2003)
  • Demo: 建立TodoItem Model,新增WebAPI Controller。使用Chrome的好用外掛-POSTMAN測試
  • POSTMAN: 可以指定HTTP Method、Content-Type、保存Request重送,功能強大,是寫Web API的良伴,真心推薦!!
  • *** Web API 2的新功能 ***
  • 屬性路由:
    * 以前需要一一加入路由特例,造成路由表複雜化
    * 路由設定寫在Controller之外,查看困難
    * [HttpGet("contact/{id:int}")]  id可以指定預設值、範圍
  • OData
    http://www.odata.org Open Data Protocol,已廣為接受,成為業界標準
    $top, $skip分頁,$orderby排序,$filter 條件式(有特殊語法,支援部分運算函數,例如: round, startswith, year...)
    WebAPI2新增的查詢$selet、$expand(一對多資料表)、$value(結果只有一筆時取得定欄位值)
  • OData Service
    讓程式碼有操作OData的能力。透過WCF OData Service,可化身為可進行LINQ操作的Data Provider[展示使用LinqPad4操作]。
    Demo: Excel 2013連上WCF OData資料摘要,拉資料出來做Pivot Table。
  • Portable Web API Client - 供Silverlight… 等種平台使用
  • 可攜式類別庫(Portable Class Library)
  • 台北市政府待認養清單Open Data- 定義資料型別,HttpClient Call API,將結果(JSON)解析映對成資料物件陣列
  • 新類別: IHttpActionResult - 實作如同MVC的ActionResult,完全控制回傳內容
    示範: WebAPI呼叫Razor產生HTML丟回
  • Single Origin Principle: CORS 解決跨網域AJAX請求的問題。NuGet安裝Web API CORS
  • OWIN & Katana
    * ASP.NET MVC與.NET Framework脫勾,可以獨立下載
    * ASP.NET Web API切斷與System.Web.dll的相依性
    Open Web Interface for .NET –> OWIN,WebServer與WebAppication間切出抽象層。Web API可以執行於符合OWIN的Host上,不一定要IIS
    Katana是OWIN的一種實作,目前已經Katana 2.0 RC
  • 四層: Application + Middleware + Server + Host,四層可任意抽換
  • OAuth 2.0
    透過第三方認證單位,服務提供者不必保存使用者的身份憑證
    Microsoft.Owin.Security實作了For Twitter、Google、FB支援
    OAuth 2.0 認證伺服器: SPA範本有一個簡單版、OWIN未來會有

HTML5 & CSS3 by Eric 上官

  • 可以用HTML5的理由:
    行動裝置瀏覽器看網頁的比例,2013 July已上升到18%,而手機平板瀏覽器對HTML5支援度相當高
    http://html5readiness.com/支援度愈來愈好
  • 為什麼要用HTML5/CSS3?
    * 減少外掛程式需求,提升網頁效能
    * Touch Friendly
    * 為更多平台app做好準備
  • 即使尚未採用HTML5開發,網站只要用到jQuery、Bootstrap、require.js… 無形中就已用到HTML5的功能
  • HTML5可以做到的比你想的更多
  • 例如: 遊戲對開發來說是較嚴苛的考驗,但HTML5已經可以開發出聲光效果十足,操作流暢的遊戲
    * Atari Arcade: 與IE合作的遊戲網頁,超炫!! 值得一看
    * Mobile ready
    * Touch friendly
    * HTML5/CSS3: SVG, Canvas, Audio, Local storage, 3D Transforms
  • 例如: 使用HTML5實現 3D虛擬實境 (WebGL)
  • IE10已支援很多HTML5新規格,而且在效能上有些強化(例如: 3D Transforms整合DirectX)
  • IE11又增加了WebGL、Fullscreen API、Web Cryptography API、....
  • http://caniuse.com延伸閱讀
  • http://ietestdrive.com IE火力展示
  • MS Virtual Academy熱門課程: HTML5 JS CSS3入門教材
  • 大量使用HTML/CSS/JS後 --> 要開始正視效能問題。IE11的F12開發者工具做了不少強化
  • DOM Explorer DOM總管
    亮點: 可以動態修改Layout中的margin、padding
  • Console
  • Debugger
    亮點: 進入中斷點時,網頁會套上灰色遮罩及暫停符號(似blockUI效果)
  • Network tool
    亮點: Request清單更容易閱讀,提供圖形化時序關係展示(似HttpWatch)
  • UI Responsiveness tool
    記錄CPU Load(區分轉譯、繪製、GC、設定樣式....)、FPS,還可追蹤負載是繪製哪一個元素產生的 <== 很強大!!
  • Script Profiler
  • Memory Tool 
    分析網頁記憶體用量、比對操作前後的記憶體更迭(物件數)、抓出Memory Leak <== 很強大!!
  • 模擬
    模擬WinPhone、平板的瀏覽器,設定直向、横向、解析度,還支援Geolocation位置模擬
  • IE11 on Win8.1,Win7上已有Preview版

SignalR Scale Out by Dino

  • SignalR 2.0-RC1 now, 預計10月RTM
  • Windows Server 2012, Win7/8的IIS預設未安裝WebSocket支援,而IIS Express內建就已支援
  • IIS預設CPU同時服務最大連線數量(Max concurrent requests per CPU)為5000(Queue),建議可修改aspnet.config提高到10000以上。但要注意,在SingalR作業裡不要做太笨重的工作。
  • SignalR的開發提醒
    * 避免blocking code,善用async, await
    * 封包愈簡單愈好(SignalR中只傳URL,讓Client自行下載圖檔)
    * Hub Instance用完即丟,不要在其中保存狀態
    * 避免使用Session: 改用Cookie、localStorage甚至DB
  • 狀態監視
    * LoadTestHarness –> 模擬SignalR負載狀態,指定頻率(msg/s)、封包大小(bytes)
    (可由github的SignalR Source中找到Sample)
    利用Performance Monitor監測系統狀態: 找出包含(WebSockets)字樣的計數器
  • 就算只服務少量客戶,CPU也會產生一定負載(兩位數)
  • 另一個工具: NuGet SignalR Utilitites(或可在Source Code中取得) signalr.exe
    參數: ipc/upc 安裝解除安裝計數器,例如: signalr.exe ipc
    PerfMon可以看到SignalR群組,要找到IIS Instance才會有資料
  • Scale Out: 三種方式 SQL、Redis、Service Bus on Azure
  • 基本上SignalR的Scale out與網站最大的差異在於現存連線中斷後如何換機器接續
  • 常見的負載模式: 廣播(Server Broadcast)、推送(Server Push)、事件(User Event Driven)、高頻訊息傳遞(例如Online Game)
  • 模式: 發佈/訂閱,將訊息放進Cache,由Worker將Cache中的資料傳送到Client端。Scale Out後,如何將資料同步到多台主機傳送出去
  • 斷線重連? 用戶端換連另一台伺服器,如何無縫接續先前的動作
  • Publication/Subscription Pattern。訊息匯流排(Backpane): 每台伺服器都訂閱訊息,其中一台伺服器將訊息放進Backpane,便同步到所有伺服器
  • 實作1: NuGet Microsoft.AspNet.SignalR.SqlServer,Start.cs註冊使用特定SQL Server(空的資料庫即可,SingalR會自動建立所需Table),非常簡單易用,但因動用DB有Disk IO,跟In-Memory機制相比速度偏慢。
  • 實作2: Redis NoSQL Server,儲存於記憶體 http://redis.iodownload,源自Linux平台,MS Open Tech將其移植到Windows,要自行下載安裝。
    NuGet Microsoft.AspNet.SignalR.Redis
    GlobalHost.DependencyResolver.UseRedis("localhost", 6379, "", "channel_name");
    效能明顯比SQL Server好很多,缺點: Redis只能單機,無法再Scale Out
  • 實作3: Azure Service Bus
    NuGet Microsot.AspNet.SignalR.ServiceBus
    在Azure開好Service Bus,取得連線字串
    GlobalHost.DependencyResolver.UseRServiceBus(…); 較適合程式在Azure的情境

再談AJAX呼叫的同步化

$
0
0

接獲報案:

某網頁透過AJAX新增資料,接著以AJAX方式取回資料清單,卻不見剛才新增的項目,重新整理網頁則正常。

經過一番檢測,確認與AJAX的非同步特性有關,循序執行AJAX呼叫的做法先前曾討論過,但這回我們改從問題剖析的角度再切入一次。

試著用以下範例重現問題。假設有一個後端ASP.NET程式如下,傳入參數mode=add時可新增字串元素,mode=clear時清除所有資料,否則傳回字串陣列內容。為求簡化,此處用Session儲存資料取代原本的DB作業。

<%@ Page Language="C#" %>
 
<script runat="server">
protectedvoid Page_Load(object sender, EventArgs e) 
    {
//在Session中放入List<string>物件
        var list = Session["Storage"] as List<string>;
if (list == null)
        {
            list = new List<string>();
            Session["Storage"] = list;
        }
        Response.ContentType = "text/plain";
        var mode = Request["mode"];
//三種模式,add加入字串元素
if (mode == "add")
        {
            var text = Request["text"];
            list.Add(text);
            Response.Write("Added");
        } //clear清除內容
elseif (mode == "clear")
        {
            list.Clear();
            Response.Write("Cleared");
        }
else//或傳回字串內容
        {
            Response.Write(string.Join(",", list.ToArray()));
        }
        Response.End();
    }
</script>

前端網頁如下,有兩個JavaScript函式add及display,分別負責以AJAX方式呼叫上述ASPX新增資料及取回資料清單。網頁則有一個TextBox供輸入文字,一顆按鈕送出、一顆按鈕重取資料顯示於下方DIV,在新增鈕Click事件裡先呼叫add()再呼叫display(),原本預期新增後可立刻顯示最新結果。

<!DOCTYPEhtml>
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>AJAX Sync Issue 1</title>
</head>
<body>
<inputtype="text"id="t"/><inputtype="button"id="b"value="Add"/>
<inputtype="button"id="r"value="Refresh"/>
<br/>
<divid="d"></div>
<scriptsrc="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.0.min.js"></script>
<script>
        $(function () {
function add() {
                $.post(
"DataService.aspx",
                    { mode: "add", text: $("#t").val() },
function (result) {
if (result != "Added") alert(result);
                    }
                );
            }
function display() {
                $.post("DataService.aspx", {}, function (result) {
                    $("#d").text(result);
                });
            }
            display();
            $("#b").click(function () {
                add();
                display();
            });
            $("#r").click(function () { display(); });
        });
</script>
</body>
</html>

結果發生什麼事? 請看以下示範,新增1後馬上看到結果,新增2後仍然只顯示1,但新增3之後才一次冒出1,2,3。

這是AJAX初心者很容易踩到的陷阱 -- 忘記AJAX呼叫都是非同步的!

jQuery.post(), jQuery.get(), jQuery.ajax()動作都採非同步執行,意思是add()呼叫$.post()新增資料後,並不會等Server端傳回結果才繼續執行下一行指令。因此,不管新增動作完成與否,display()呼叫$.post()查詢的Request便已送出,甚至可能比新增資料的Request更早執行完畢,在此種狀況下接收到的便是尚未新增完成前的結果,不包含新增資料。

由Request時序圖可清楚看出問題所在。在下圖中,我共新增了兩次資料,每次新增都會產生兩個Request(一個mode=add,一個查結果),發現了嗎? 兩個Request幾乎是同時送出,而執行時間長短不一,第一次新增花0.758秒,顯示花0.259秒;第二次反過來,新增只花0.012秒,但查詢耗時0.510秒。當新增很快完成,查詢就能抓到最新結果;反之就會看到未新增前的狀態,足以解釋為什麼前述示範中1,3新增馬上看到結果,2新增後卻沒更新。

要解決這個問題,最簡單的方法就是強迫查詢必須在新增作業完成後再執行,將程式放進jQuery.post的success Callback函式執行,即可滿足需求。

稍加修改程式,在add函式中加入cb函式參數,並將其置入$.post()待收到Server回應後呼叫,而在呼叫add時,傳入呼叫display()的程式當成cb參數,即完成串接:

        $(function () {
function add(cb) {
                $.post(
"DataService.aspx",
                    { mode: "add", text: $("#t").val() },
function (result) {
if (result != "Added") alert(result);
if (cb) cb();
                    }
                );
            }
function display() {
                $.post("DataService.aspx", {}, function (result) {
                    $("#d").text(result);
                });
            }
            display();
            $("#b").click(function () {
                add(function () { display(); });
            });
            $("#r").click(function () { display(); });
        });

重新執行測試,我們可以看到第二個Request的執行時間一律在第一個Request結束後,解決了有時看不到剛新增資料的問題。

不過,透過Callback參數的做法有個缺點,例如: 還有工作要排在display()完成後進行,Callback層層相套會變成:

add(function() { 
  display(function() { 
//…blah… 
  }); 
});

看起來有點噁心,對吧? 該jQuery.Deferred上場了!

        $(function () {
function add() {
return $.post(
"DataService.aspx",
                    { mode: "add", text: $("#t").val() },
function (result) {
if (result != "Added") alert(result);
                    }
                );
            }
function display() {
return $.post("DataService.aspx", {}, function (result) {
                    $("#d").text(result);
                });
            }
            display();
            $("#b").click(function () {
                add().done(function () { display(); });
            });
            $("#r").click(function () { display(); });
        });

我們讓add()、display()拋回$.post()所傳回jQuery.Deferred物件,原本要當成參數的Callback便可改放在done()之中,寫成: add().done(function() { display(); });,順序接在函式之後更加直觀。如果要串接更多作業呢? 去吧!pipe() .then()就決定是你了: (延伸閱讀: 以jQuery循序執行AJAX呼叫,並依結果決定是否繼續) [2013-10-22更新: .pipe()自18.0起已列為過時,應改用then(),感謝Ammon回饋。 ]

            $("#b").click(function () {
                add()
                .then(function () { return display(); })
                .then(function () { return $.get("Lab1.html"); })
                .then(function () { return $.get("Lab2.html"); });
            });

這樣子處理同步是不是優雅多了呢?

正被命運鎖鏈一步步推向前端火坑的朋友們,建議花點時間熟悉一下jQuery.Deferred,保證值回票價。

【茶包射手日記】TEMP指向RAMDisk導致Windows更新失敗

$
0
0

前幾天,工作機出現Windows更新重開提醒,從善如流,重開後卻看到駭人畫面:

Failure configuring Windows updates
Reverting chanes.
Do not turn off your computer

系統還原修改後自動重開,卻還是出現類似訊息(有Preparing to retry等訊),之後再度自動重開。心頭一涼,該不會Windows 2008R2就此癱瘓吧? 所幸,歷經兩次自動重開後恢復正常登入,看似一切正常,不禁讚美Windows自我修復能力不錯,有種死裡逃生的僥倖感。

今天適逢Visual Studio 2013 RTM,吃飯的傢伙當然也要灌頂一番。安裝VS2013得先升級到IE10,這才發現上回更新失敗自動還原後問題並沒有根本解決,Configure失敗的Windows Update仍在,卡住了其他的Windows更新,未更新完全就無法安裝VS2013。

由Windows Update的View update history展開追查。

由歷史記錄可看到有問題的更新是KB2864202。

循著編號KB2864202及Failure configuring Windows updates Reverting changes關鍵字爬文,很幸運地查到一篇網友經驗分享,提到TEMP暫存資料夾放在RAMDisk會導致類似問題,剛好與我的情境相同。

將TMP及TEMP路徑調回一般硬碟重新再試,Update果真順利安裝完成,感謝萬能的Google大神跟網友無私的分享。

更新問題順利解決,Visual Studio 2013裝好,卻已是十幾個小時之後,但這又是另一段茶包射手故事...

【茶包射手日記】惱人的IE10 9C59安裝錯誤

$
0
0

地表上最強大的開發工具 -- Visual Studio 2013於10/17 RTM了!!

公司的工作機是吃飯的傢伙,豈有不馬上升級的道理? 先前在家裡Windows 8安裝順暢無比,沒想到要裝在Windows 2008 R2卻歷經千辛萬苦,如赴西天取經一般波折...

我的Windows 2008 R2要安裝Visual Studio 2013,有兩項事前準備工作: 完成最新的Windows Update更新並升級IE10。因RAMDisk TEMP導致Windows Update更新失敗的鬼故事昨天已經說過了,而為了配合使用者環境,工作機的IE版本仍停留在IE9,這回不升級不行(反正IE11已隨Win8.1與VS2013同一天推出,公司IE版次推升也是早晚的事),Just Do It!

原本以為下一步下一步就可以搞定的IE10安裝,卻意外磨人,帶來一連串令人挫折的安裝經驗。

由網站下載IE10安裝時出現"Internet Explorer did not finish installing"錯誤訊息,試著透過Windows Update安裝一樣失敗收場,但多了個Code 9C59錯誤訊息:

由Windows\IE10_main.log可以看到詳細錯誤訊息: (經比對,不管使用安裝程式或是透過Windows Update安裝,背後發生的錯誤是相同的)

00:11.871: INFO:    Installing with the downloaded package. C:\Windows\TEMP\IE1D91F.tmp\IE10-neutral.Downloaded.cab
00:11.871: INFO:    Launched package installation: C:\Windows\SysNative\dism.exe /online /add-package /packagepath:C:\Windows\TEMP\IE1D91F.tmp\IE10-neutral.Downloaded.cab /quiet /norestart
00:18.080: INFO:    Process exit code 0x80092004 (-2146885628) [Cannot find object or property. ]
00:18.080: ERROR:   Neutral package installation failed (exit code = 0x80092004 (2148081668)).

有明確的錯誤訊息,理論上很快能在網路找到答案,沒想到相關討論滿坑滿谷,遇到問題的人很多,各式建議五花八門,卻沒有明確結論或解法。依我推測,這個看似清楚的錯誤訊息其實並不明確(說不定像"發生一般性的錯誤"一樣含糊),背後成因很多,大家實際遇到的魔王、小妖不盡相同但訊息卻相似,才會出現某人說有神效的療法,其他人試了連汗毛都沒搖一下。微軟甚至出了一份IE10安裝茶包指南,靠! 是有沒有這麼難裝?

總之,效法神農嚐百草,我試了以下做法:

  1. 解除特定Windows Update: KB2422556
  2. 升級顯示卡驅動程式
  3. 使用Windows更新整備工具修補更新
  4. 用pkgmgr清除更新整備工具回報損壞的Microsoft-Windows-IE-Hyphenation-Parent-Package及Microsoft-Windows-IE-Spelling-Parent-Package。[有人建議連先前版本(IE8, IE9)的套件也一併移除,擔心胡亂清除會有後遺症,我只移了確定破損的項目]
  5. 使用SFC /SCANNOW修復損壞的系統檔
  6. 學會看CBS.log、dism.log、CheckSUR.log...
  7. 進入Clean Boot模式再試
  8. 移除IE9重新安裝一次IE9
  9. 有人說要重裝OS... (花惹發! 雖被茶包磨到歸藍波火,但拎杯神智還清楚,沒照辦)
  10. ...

上方法全都試完,問題依舊... 不,是更糟了!! 移除了IE9打算重裝,但卻發現IE9重裝也失敗。這下可好,不但沒升到IE10,反而退回IE8,HTML5專案要怎麼測試? 長嘆一口氣,絕望到想輕生...

天無絕人之路,排除IE9無法安裝問題時,在微軟技術支援網站學到一招:

FORFILES /P %WINDIR%\servicing\Packages /M Microsoft-Windows-InternetExplorer-*9.*.mum /c "cmd /c echo Uninstalling package @fname && start /w pkgmgr /up:@fname /norestart"

先前網路上看過pkgmgr移掉IE套件的解法,但我只移除了偵測到損壞的項目,而由上述做法推敲,將該版本所有套件都清除才能解決問題(而且是安全的),有官方文件背書決定放手一搏,將上述Script中的*9.*.mum改成*10.*.mum,執行清除後再重新安裝。喵的,IE10安裝成功,我感動到都要哭了~

終於,Visual Studio 2013在工作機上安裝就緒,Let's Party!

2013艋舺馬拉松~

$
0
0

第11馬,2013艋舺馬拉松。

賽前與教練團討論,總教頭謎之聲強力主張: 因後面尚有貓空及田中馬馬相連,加以腳傷未瘉及本屆"辦桌"重現江湖,故為本場賽事定調,提出16字箴言: 戒急用忍,保全實力;從容不迫,主攻辦桌。聽到總教練這麼說,心中無比寬慰... (盛竹如旁白: 開心接下總教練指示,這時的黑大並不知道,自己正一步步墮入廢柴深淵~~~)

五點多來到華中橋,天色仍昏暗,辦桌用的200組桌椅已擺放就緒。起跑前夕烏雲籠罩,下起一陣雨(右上照片還剛好拍到一根雨絲哩),有點擔心會重演去年冒雨完賽的考驗,所幸鳴槍前一刻雨勢轉小,事實上,老天爺今天還賞了個晴時多雲的好天氣。

今年的賽道要來回折返三趟,河濱道真的窄了點,排在5:00-5:30實力區,起跑後兩公里人潮濃得化不開,速度都在七分速以下。不過對我這種後半段打算當步兵的涼涼組來說,這種小小延遲根本不需要放在心上。隨著接近中午,天氣愈來愈好,一片藍天白雲,但雲層削弱不少日曬,造就出不錯的跑步天氣。

另外,我要大推華中橋下的幾支大音響喇吧,大聲放著電音舞曲,每回經過都忍不住衝上一小段,這才發現原來"凍次凍次"跟跑步這麼搭, 哈!

補給站精彩度一如往昔,運動飲料與水是基本配備,後來連沙士、雪碧也出籠,還夾雜一些蠻牛。除了香蕉、葡萄、香瓜、鹹蛋外,今年還出現豆干與香腸(沒給蒜頭,好可惜),連必魯(Beer啤酒)都上陣,根本是下酒菜組合啊! 幸好這些沒出現在同一攤,否則難保不會有跑友在路邊"罷掀啊罷掀、棄翹啊棄翹"划起酒拳。

去年的雨中艋舺馬,飛小魚的"跑步吧! 人生"網路廣播伴我全程。今天在最後十來公里,忽然聽到對向跑友叫了聲"小魚 加油",轉頭一看,飛小魚就在我後方(號碼布寫的就是"飛小魚"耶! 酷~),去年只聞其聲,今年見到本人(下圖右上紅衣背影),分外有趣。最後,以5:03:38秒完賽,還完晶片20分鐘就領到成績證明,快速如昔,終點站的冰仙草超好喝,忍不住連乾數碗。

重頭戲來了,辦桌登場。大會事先調查過跑友的預估完賽時間,將差不多時間的跑友安排在一桌,原本以為大家素不相識,湊在一起吃飯會冷場尷尬,但顯然多慮了。每個人都有滿滿的跑步經,話匣一開聊也聊不完! 哈,如果有機會跟不認識的Developer併桌吃飯,我們應該可以從機車客戶、水母PM聊到機房乖乖奇譚,應該也會一樣熱絡吧?

今年的獎牌為銀色亮面款,十分好看,私人收藏再添一枚漂亮獎牌。

【補記】

跑前幾天GPS錶的USB孔防水蓋斷了,晚上在客服網頁留言,隔天早上便接到客氣親切的客服信件請我提供地址,再隔兩天零件備品就送到了,在賽前就換新搞定。成本或許不到十元,但客戶感受的價值絕對遠大於此,是一次很棒的客服經驗。

VS2013使用IE10偵錯出現錯誤: 找不到sourcePath屬性

$
0
0

使用VS2013 RTM偵錯網頁出現錯誤:

Unhandled exception at line 77, column 9 in httq://localhost:49498/9c5e62d30ac64961af17a184019bb19e/browserLink

0x800a138f - JavaScript runtime error: Unable to get property 'sourcePath' of undefined or null reference

爬文查到是WebEssentials2013的Bug,而作者在我發現問題3小時前剛完成修正(熱騰騰的,當然還沒Release,要稍等):
[2013-10-24更新] WebEssentials 2013已釋出ver 1.1.1,問題被修正囉。

這才發現WebEssentials是個Open Source Project,而地球另一端的神人正在深夜出擊維持宇宙和平,透過github隨時還能追蹤即時動態。當年用紙卡打洞Coding的程式設計師前輩們,做夢也想不到幾十年後所謂的程式開發是這般光景吧? :P


【.NET隨堂考】object == string比對

$
0
0

來個.NET隨堂測驗,請解釋以下現象:

在以上程式,bool test = d["A"] == "1"所得結果為true,代表二者相等;但由Immediate Window顯示d["A"] == "1"卻是false,為什麼?

首先聲明: d["A"] == "1"的寫法有問題! d的型別為Dictionary<string, object>,故d["A"]所傳回型別為object,對object進行==運算時,等同Object.ReferenceEuals(),只有當前後二者指向同一物件時才會傳回true。由MSDN對於 == 運算子的說明:

    1. 對於預先定義的實值型別 (Value Type),等號比較運算子 (==) 在運算元相等時傳回 true;否則傳回 false。
    2. 對於 string以外的參考型別,若兩個運算元參考到同一物件,== 會傳回 true。
    3. 對於 string 型別,== 會比較字串的值。

程式的原意是要做字串比較,卻忽略了d["A"]傳回的是object,當與"1"比較,內部進行的是Object.ReferenceEquals() [適用上述第2點]。在這個例子中,正確的做法是將d["A"]轉型為string再進行 == 比對,比較的才是二者的字串內容。

事實上,針對這類不適當的 == 使用,Visual Studio會提出警告: (只是警告而非錯誤,程式可編譯、執行)

Possible unintended reference comparison; to get a value comparison, cast the left hand side to type 'string'

很好,我們已指出d["A"] == "1"寫法不當,只有二者指向同一物件時才會得到true。在實測中,Immediate Window檢測d["A"] == "1"為false可以被理解,但test = d["A"] == "1"卻得到true,又是怎麼一回事?

這要從字串常值(String Literal)說起,本草綱目MSDN有記載:

字串常值的型別是 string
每個字串常值並不一定會產生新的字串執行個體 (Instance)。依據字串相等運算子 (章節 7.9.7),當兩個或是多個相等的字串常值出現在相同組件時,這些字串常值便會參考到相同的字串執行個體。

由此可知,程式碼裡出現兩次"1",編譯器會讓兩個"1"字串常值指向同一個字串執行個體,在此情況下,d["A"]與"1"將會指向同一個字串執行個體,Object.ReferenceEquals()傳回true,==運算子的執行結果為true。而在Immediate Window操作所輸入的"1",由於是偵錯期間動態編譯執行,故會指向另外的字串執行個體,故==比對得到false。

解題完畢,大家答對了嗎?

Windows記憶體都用到哪裡去了?

$
0
0

不知大家是否跟我一樣,心中有個懸而未解的謎:

開啟工作管理員,看到記憶體已被用掉14GB,所剩無幾。切到程序列表逐一清點: Visual Studio說它只用了240MB、一堆Chrome個個聲稱自己只耗用不到100MB、IE強調自己克勤克儉只微取50MB、SQL Server大聲喊冤: "不要看我,我超省的! 只用不到75MB,比Chrome還少"... 大伙紛紛跳出撇清。所有程序的耗用量加總起來不到3G,總用量卻高達14G! 短少的部分到哪裡去了? 有程序貪贓枉法,以多報少? 還是某個神祕記憶體大戶私吞巨款潛逃,從程序清單神隱?

以下圖為例,當時總記憶體使用量約14GB,正在執行的108個程序依記憶體用量由大到小排序,前10名加起來約1.2G,第11項-108項由50M開始遞減。好,就算退一萬步,把後面的98項每個都當成50M,也才4.8G,1.2+4.8=6G,總記憶體被用掉14G,相差絕對大於8G,到底被誰偷走?

茶包一哥出身的無敵工具箱,SysInternals,有個破解記憶體之謎的好工具 – RAMMap。執行後只要幾秒,原本疑雲滿天的記憶體舞弊懸案,頓時真相大白,絲絲分明!!

原來,工作管理員(Task Manager)列出的只包含各程序專屬的私用記憶體區,並不包含系統層次的記憶體使用統計,例如: 跨程序的共用記憶體區、檔案系統快取、驅動程式佔用(包含RAMDisk)... 等,都未列在其中,就是數字差異的來源。

在我的案例(如下圖),Process Private(就是工作管理員裡所有程序記憶體用量的加總)約2.3G,Mapped File是檔案快取耗掉6G為最大宗、Metafile也屬檔案快取的一部分,用掉1.3G、而Driver Locked是驅動程式使用部分,由於我切了4G做RAMDisk,加上含其他驅動程式應用共4.8G。Active一欄的加總結果,不多不少,就是14.4G,與當下工作管理員顯示的總記憶體用量完全吻合。(註: Mapped File及Metafile的6G + 1.3G屬快取性質,會在記憶體不足時移給程序使用;當記憶體充足,Windows則會盡量使用快取提高效能)

久懸心中的謎團總算解開,了卻一椿心事... (謎之聲: 有這麼嚴重嗎?)

最後補充,Technet網站有篇介紹文章,詳細說明了上圖各分類及欄位的定義,整理如下供大家參考: (我不是ITPro專家,對OS運作原理了解有限,如有曲解謬誤,請不吝指正)

  • Process Private: 分配給單一Process專用的記憶體
  • Mapped File: 用來儲放檔案內容快取(Cache)的記憶體空間
  • Shared Memory: 標註給多個Process共用的記憶體分頁(Page,記憶體管理單位)
  • Page Table: 用來描述虛擬記憶體位址的分頁表(裡面是一筆一筆的PTE,Page Table Entries)
  • Paged Pool: 允許移至磁碟的核心集區記憶體(Kernal Pool Memory)
  • Nonpaged Pool: 不允許移至磁碟的核心集區記憶體
  • System PTEs: 與I/O空間、核心堆疊、記憶體描述清單等系統分頁相關的PTE
  • Session Private: 登入工作階段相關的記憶體
  • Metafile: 是系統快取的一部份,包含NTFS Metadata(包含MFT及其他NTFS Metadata檔案)。在MFT中,每個檔案屬性記錄佔用1K,而一個檔案至少有一個屬性記錄,再加上其他NTFS Metadata檔,當檔案數眾多,這塊會很快速成長。
  • AWE: 啟用Address Windowing Extension技術所使用的相關記憶體空間(較常應用在SQL或其他DB)
  • Driver Locked: 驅動程式鎖定的實體記憶體。多用於I/O的暫時性小量應用,如果有裝RAMDisk,也會算在這一區。
  • Kernel Stack: 核心執行緒推疊,執行緒愈多,用量愈大。

每項分類都有以下欄位:

  • Active: 正在使用中的實體記憶體分頁(Process Working Set或System Working Set)
  • Standby: 留在實體記憶體但暫不使用的分頁,保留供後續能快速重覆利用
  • Modified: 與Standy類似,但內容被修改過,重覆使用前要先回寫到磁碟機
  • Modified no write: 與Modified類似,但標註為不需回寫到磁碟
  • Transition: 在分類之間轉換的分頁
  • Zeroed: 內容已清空可供使用的分頁,系統剛開機時明顯增加,隨著使用一段時間逐步轉為Standby
  • Free: 可以使用但殘留先前資料的分頁,使用前需先轉為Zeroed
  • Bad: 標註損壞的記憶體

2013貓空半馬~

$
0
0

同一場路跑連續四年參加(1, 2, 3)是什麼感覺? 賽前一如往例找總教練謎之聲討論戰略,他忙著看半澤直樹,連頭也沒抬只丟一句" 隨便你啦! 別吵我"把我打發... 如果這不是"平常心",那什麼是"平常心"呢?

其實也是,算算已跑完十來場全馬,雖然常醉翁之意不在酒,光顧著吃喝賞景,視成績如糞土(實際上我的成績也真的跟糞土所差無幾),但體驗連跑五六個小時的次數一多,對於兩個多小時就結束的半馬就不怎麼放在心上了。

今年路跑忽然夯了起來,一些奇奇怪怪的灑粉螢趴無嘴貓主題路跑都一律被秒殺,往年都採親洽或通訊報名的貓空路跑,或許是怕人數失控,首次限定人數並改採網路線上報名。幸好還不致熱門到一票難求,不然連在"自家後院"辦的路跑都報不到,肯定很悶。

照慣例,我當然又充分運用主場優勢,七點才從家裡晃晃悠悠散步前往會場,連寄物都省了。天氣不錯,多雲偶有陽光,氣溫在20度以下,好天氣,起跑後感覺狀況不差,想想連續幾場全馬都在醉生夢死,墮落夠了,今天只跑半馬,就拼個PB振奮一下吧!

從一起跑便催足油門,甚至好幾次過水站而不入(靠! 我變了),除了在水站喝完水會走一小段緩和,一路都是連跑帶衝! 上坡時也維持小跑步,盡力避免被超車。跑到氣喘吁吁,征服5K長上坡,手表顯示時間為31'52",已打破自己攻上杏花林的最佳記錄。前5K的表現超出預期,後半段更是陷入力破PB的熱血模式無法自拔,沒有閒情逸致拍照,連身旁的其他跑友都沒心思多看,只封閉在墨鏡加MP3圍起的小宇宙裡跑跑跑~

最後5K,歷經一連串高速下坡,右小腿有些緊繃,幾度彷彿已站上抽筋的懸崖邊,靠著暫時減速並調整跑姿才避免爆發。終於撐到政大後門,還剩最後1公里多,一咬牙狠下心,首次完成用跑步征服"後門-蔣公銅像-藝文中心"斷魂坡的壯舉。藝文中心過後還有約400公尺的下坡加繞場200米,右小腿再數次揚言跳崖,幸好仍Hold得住。過終點成績1:57:39,刷新了去年所創下1:58:30的最佳記錄,勇破半馬PB! (灑花)

雖然只快了不到一分鐘,但這就跟Performance Tuning一樣,到達硬體瓶頸後,想再提升一點點都困難重重... 想想,破PB這種壞習慣還是早日戒除為宜 orz

配速分析: 5:25 / 5:18 / 6:36 / 6:55 / 7:09 / 5:57 / 6:13 / 5:22 / 5:31 / 5:43 / 5:09 / 5:07 / 5:10 / 5:17 / 5:20 / 5:25 / 4:39 / 5:00 / 4:50 / 5:16 / 5:31 / 4:26

Visual Studio 2013開啟HTML檔設計檢視

$
0
0

VS 2013 RTM的隔天,我的工作環境就迫不及待全升級成Visual Studio 2013,VS2012的功能都有,再多了BrowserLink、CodeLens等好用功能,Coding樂趣加分不少。

今天倒是發現VS2013有一項改變: 開啟.html檔案時預設不再提供Design/Split/Source三種檢視模式的傳統網頁編輯器,只剩HTML編輯功能。試著改用VS2012開啟.html檔,證實VS2012仍支援設計檢視,此點算是VS2013的行為變更。

說實在話,使用WYSWYG方式在設計檢視拖拉元素固然方便,但背後自動產生的CSS及HTML屬性甚為噁心,不符合當前HTML應力求簡潔乾淨,樣式呈現交由CSS全權操控的設計概念;更何況現今網頁很多需依賴JavaScipt輔助才能正確呈現(例如: Knockout.js、AngularJS),從Visual Studio設計檢視多半已看不出所以然,這大概是VS2013推出BrowerLink,把瀏覽器也整合成IDE一部分的重要理由吧! (關於BrowserLink,Sccot Hanselman有個簡短的Demo影片,值得一看。但需注意,BrowserLink需要Web Application (Web Site Project哭哭),若要讓靜態HTML檔案也能使用BrowserLink,需要額外設定。)

話雖如此,某些時候還是有寫完HTML想快速看看約略樣貌的需求,此時Split分割檢視還挺方便的。找了一下,在Visual Studio 2013仍能用傳統設計檢視編輯.html,方法如下:

1.在Solution Explorer對.html按右鍵選取"Open With..."

2.選取HTML (Web Forms) Editor

3.設計檢視與分割檢視回來囉~

【茶包射手日記】Word VSTO程式問題兩則

$
0
0

先前寫過Word套表服務,透過C#程式呼叫Word進行文字置換並轉存PDF,包裝成Windows Service執行並透過ASP.NET Web API接受需求。

程式在開發環境與測試環境運作良好,部署到正式環境Windows Server 2003卻遇上麻煩。

首先,套表程式的Windows Service先前在開發機Windows 2008 R2跟測試台Windows 2003的執行身份都是設成Local System,運行無誤,但正式台Windows 2003上Word存檔時卻一直冒出:

System.Runtime.InteropServices.COMException (0x8001010A): The message filter indicated that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER))

直到我將執行身分改換成網域帳號上述錯誤才消失(但為何Local System不行? 原因成謎),接著卻冒出另一個錯誤:

Exception from HRESULT: 0xC0000005 Stack Trace : at Microsoft.Office.Interop.Word. Find.get_Replacement() ...

很幸運地找到一篇相同情境的MSDN論壇文章,追到一篇KB指出問題是Excel 95的COM GUID與Word Interface打架(詳情可以看這篇),KB提供了兩種解決方式:

  1. 程式改採Late Binding
  2. 使用regtlib.exe重新註解Word元件壓過Excel 95的設定

非常不爽為此改寫程式,一來工程浩大,二來為了Excel **95**改程式感覺太窩囊,故決定動用regtlib撥亂反正!! 有趣的是,找不到regtlib.exe,上網查才發現它是Visual Studio 6.0時代的產物(2007年出廠... orz),Windows 7之後改由.NET 2.0 Framework的regtlibv12.exe接任,找到Word元件(MSWORD.OLB)所在路徑,執行以下指令註冊:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>regtlibv12.exe "C:\Program Files\Microsoft Office\Office12\MSWORD.OLB"
Registration of C:\Program Files\Microsoft Office\Office12\MSWORD.OLB successful
.

問題排除,搞定收工~

PS: 覆寫Excel 5.0介面會有後遺症嗎? 依KB的說法:
Several globally unique identifiers (GUIDs) that are used by Word for its interface identifiers were used by Excel 5.0 for an older object model that is now obsolete.
衝突的GUID用於Excel 5.0 Disinterface,已被宣告為過時避免使用,理論上風險不高,如真有問題,那就是命囉~ XD

Viewing all 2458 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>