2013年10月6日

Microsoft TechDays 2013

10/06 Update SQL Server影片已經放到最下面,有需要的可以略過上面這些碎碎念XDD,直接看最下面的影片。

TechDays已經是小弟我每年必定參加的年度大拜拜了,甚至以前要去新公司面試時,對方主管問我,有沒有甚麼要問的問題,我一定會必問的問題就是"每年我可以請假三天去TechDays嗎?";而到今年2013,小弟也已經第四年參加了這個隆重的聚會,而每次的TechDays,都是小弟一次又一次的目標與轉淚點。

記得第一次的TechDays,由各個神人前輩們給小弟的震撼,體驗到,來上技術課程是那麼的充實與愉悅,也決定每次都要來。

記得第二次的TechDays,小弟與神人們的第一次接觸…( Orz,不是ET那種接觸啦.. ),更加的決定,自己以後也要站上這大舞台。

記得第三次的TechDays,從MVP和社群的角度出發!!

而這次,第四次的TechDays,小弟終於圓了夢,站上了TechDays的大舞台上。

所以,今年的TechDays,雖然忙到快要死掉,但對小弟來說,也特別有意義。

而今年,小弟一樣又來參加了這個聚會,而且這次不用再特別請假XDD,公司讓全公司的同仁一起參加去!! ( 根本就是變相的員工旅遊嘛= =… )

一樣,每年都必定要拍的一個看板,而今年Study4.TW也一樣很榮幸的,參加了合作社群的攤位~

IMG_1778

既然是合作社群,所以小弟前一天,還是要到場看一下攤位狀況,並且測試機器,而今年,我們攤位終於不是躲在1F的後方,而是移到了2F的位置;而台灣最大的SQL PASS社群,這次也由百敬老師和Terry哥坐鎮,擺設了SQL Server問答攤位區XDD ( 說到這個,SQL PASS後面那箱帽子,快讓我的半條命去了- -… ),而當天抵達後,現場還在施工…

IMG_1756IMG_1757

完成圖如下,到了正式開始的那天,整個氣勢都出來了!!而這次少了IT幫幫忙等社群,只剩下我們三個了 QQ…

SONY DSC

而我們攤位,一樣贈送的贈品還是去年的書籤…

SONY DSC

當然,還有印製新版的書籤…沒辦法,我們是公益社群,後面沒有經費QQ…很難和一般大廠,闊氣的拿出高貴的贈品…,另外,順便偷拍一下百敬老師,和他那傳說中的超重量級筆電…

SONY DSCSONY DSC

今年的水一樣有貼上TechDays的標籤喔!!

SONY DSC

比較可惜的是,今年比較沒甚麼機會去逛攤位… ( 完全沒空…QQ.. ),而今年,主要就是平板的Surface為主 (其實說Surface為平板也不恰當…畢竟他的硬體可是媲美一台筆電… ),然後一樓一樣有超大型海報…

SONY DSCSONY DSC

而今年的主題,主要就如1F的旗子一樣,有Office 365、Windows Azure,和超熱的Visual Studio 2013和Windows Server 2012 R2。( 當然Win 8.1也是主力啦!! )

SONY DSCSONY DSC

就如小弟說的,今年沒時間去逛攤位,也沒時間收集神人照幫大家拍照;第一天剛好在樓下遇到Ruddy老師和ChiaChi,所以就順手一拍XDD,至於Ruddy老師就不用說了…業界資深之程度,除非老師退休去騎腳踏車外,大家要超越大概也很難了吧…至於ChiaChi…每次他說很簡單的問題,一定都是不簡單…。

SONY DSC

Ruddy老師這次講了三場,老實說,場場都爆滿,更厲害的是,能把沒甚麼技術的Yammer,講出有深度,有涵量,這真的不是一般人能辦到…結果Yammer那場不但爆滿到位置座不下,更體會到…甚麼是24hr的工作方式XDDD

IMG_1777

第一天的主會場,和第二天的VS發表會,這次Microsoft也是一樣重裝上陣,小弟我也可以看到Dann哥,在這次的TechDays很努力地推廣Scrum,希望能讓台灣的軟體,更上一層樓。

SONY DSC

當然,身為VS和TFS工具台灣之神的Franma ( 肥馬 ),也負責了TFS的部分~,而比較有趣的是,這次TechDays請到宋少卿來幫忙主導過程,老實說,有了相聲高手來助陣,這次TechDays也多了一分趣味,而非那麼死板的技術呈現( 只是不知道花了多少錢- - )。

IMG_1775IMG_1763

其實,就如前面說的,這次活動,小弟大概就只拍這些照片了…也沒時間趴趴走了。

最後幾張照片,用來紀念小弟自己的里程碑….這次TechDays也收集到了三大證,尤其是中間那張,更是小弟夢寐以求的~^^~

SONY DSC

而這次擔任講師,也很感謝Microsoft官方的邀請,還有Tom哥的推薦,讓我可以站上舞台上,講Azure上的SQL Server和SQL Database,而為了這次的活動,也順便把Macbook順便升級一下Ram…變成16G的筆電…. ( 因為8G跑不動了QQ ),此外,我想我也是少數不怕死,拿著Macbook上台講課的講師之一QQ…. ( 其實我是要證明,Microsoft現在不是以前的封閉了!! (點頭無誤))

IMG_1748

當然,這次的準備過程中,其實還是有很多不滿意的地方,例如,講課內容的重心在Developer上,但準備的方面還是著重於SQL Server AlwaysON和SQL Database,比重的拿捏還是沒有拿捏得很好;其次,這次的Demo也比預期的少很多,梗的鋪設也比較少…

但這也是有原因的,因為被"本次大會最有梗"之神人Eric拍了一下,所以身陷了TechDays魔咒…這個魔咒的出處。是因為中午吃飯的時候,Eric哥和我說…他準備這次TechDays,VS2013出了一堆問題、Azure出了一堆問題,所以我就和他說…你著個賽人,離我遠一點…然後他就拍了我一下,然後也拍了Demo哥一下…於是我們就深陷魔咒之中…

我遇到的狀況,建立好的OS,整個給我掛掉,掛掉就算了,大不了重建,結果Azure不給我重建…於是整個搞了三天,講課前一天我甚至沒睡覺,最後掏出自己的信用卡,講課當天早上,才把Demo復原…所以,未來還有機會…一定要先準備好錄好影QQ…

至於Demo哥…據說他上課的時候VS2013一直當掉…甚至講完課後,整個筆電掛了…Orz…

這樣就結束了嗎??..No…還有Dino哥XDD,當天他也是吃飯名單之一,他是在Review投影片的時候,不小心誤刪…不過他做的比較遠,所以波及比較少XDDD

在來是Franma,據說當天Eric哥站在Franma後面,Franma就在Demo前,服務起不來..

最後是KKBruce,Eric哥第三天拍了他之後,全桌鼓掌XDDD,然後KKBruce有一個Demo就不來了…

最後,附上這次最神之八卦神人,Eric哥的照片….感謝他,讓我這次Demo做了N遍….

SONY DSC

但不管怎樣,還是以傳說的David老師,最為傳說XDD

當然,以上歸八卦,不果魔咒怎樣的,自己還是要多多檢討這次的缺失和講不好的地方,讓自己更加精進~~

最後,這次還是要感謝Microsoft長官們、邀約的Tom哥,讓我一圓站上舞台的夢想、公司老師老闆們的支持、Study4.TW的朋友們、MVP Group前輩們的鼓勵,小朱前輩的盯場QQ、講課當天陪著我的Terry和James和Justin,中午幫我買便當的Franma,放棄去聽百敬老師、David老師、KKBruce,而願意來聽課的大家們,還有在坐月子,忍受我整個連假一直在調整投影片和Demo,忍受我徹夜不睡覺敲敲打打的老婆和兒子"們" ( 另外一個剛出生,可能感覺不到XDD ),謝謝大家!!!~

最後的最後,附上在課堂上說要給大家看得,一鏡到底,未剪輯的Azure SQL Server AlwaysON安裝過程…,這個安裝過程是當天早上安裝的流程,所以滑鼠也會不斷閃動,也請大家見諒,最後,還是謝謝大家的支持,謝謝。

謝謝大家,TechDays 2014再會~~

ASP.NET MVC - Web API OData Batching 406 Not Acceptable 錯誤

這篇稍微簡單的紀錄一下,浪費掉我打電動認真陪家人的兩個放假天,那話也說在前面,根據查到的狀況,看起來,應該是Web API的BUG,但官方並沒有承諾甚麼時候會改好,只能誠心祈禱RTM的Web API 2能順利修正,所以,未來如果有遇到類似的問題,大家可以試著升級成最新版本看看。

最後,和這件事以外的另外一個補充,先不論這件事情,目前Web API OData的支援程度還是有很多地方沒完整實現,所以很容易踩到地雷,當然,官方還是在努力地將Web API越做越好;而如果害怕擔心,對於WCF又已經熟到不行的朋友,那可以考慮直接用WCF時做OData Endpoint吧=v=。

那這個問題是甚麼??..

主要是小弟在整合OData方案的時候,搭配JayData和Datajs來協助對OData Service來進行資料的管控與存取,一般的使用上,都沒遇到甚麼多大的問題 ( 也吃了不少苦….因為現在文件幾乎都是針對WCF… ),但最近在處理以下Code的時候,缺發生了一個比較棘手的問題;喔,對了,看不太懂此Code沒關係,簡單的說,我就是要利用JS來新增資料,先用add去新增到Context,然後再用JayData的Framework來saveChanges方法,一次更新。

var CustomerAgreementType =
    new InternalIMS.Model.CustomerAgreementType({ CustomerID: fkID });
CustomerAgreementType.ModifyDate = Date.now();

mydatabase.Agreement.add(model);
mydatabase.CustomerAgreementType.add(CustomerAgreementType);

mydatabase.saveChanges(function () {

});

上面那個Code看不懂其實沒關係,總之,我想要的效果,就是利用一次的Request,來送出兩個新增的訊息( 以前的話,要送出兩次Request來達成新增。)

而之前的Web API連此功能都沒有…Orz..在Web API 2也已經提供了此功能,此功能就稱為Batching,有興趣的朋友,可以看這篇架設。(其實也只是多設定一行而已…)

但真正的問題在後面,我們先來看一下,要新增兩筆資料的Requst,如下圖,我們可以看到一次發送兩筆POST ( 因為我們要新增嘛… )

image

但當我們看Response的時候,我們可以看到發生了406 Not Acceptable錯誤。

image

當下發生這個錯誤,真的是把我搞死…後來才查到,原來是OData服務網址的錯誤,也就是這張圖的這個地方。

image

是的,其實是URL錯誤,上圖的URL的位置其實是http://localhost/Agreement和http://localhost/CustomerAgreementType,但實際上,我們的位置卻應該是在localhost/OData/Agreement之下…所以會返回406錯誤。

為了驗證真的是這個問題,所以小弟我直接用datajs來進行測試,Code如下。

OData.request({
    requestUri: "/OData/$batch",
    method: "POST",
    data: {
        __batchRequests: [
            {
                __changeRequests: [
                  { requestUri: "/OData/Agreement", method: "POST", data: model }
                ]
            },
            { requestUri: "/OData/Agreement", method: "GET" }
        ]
    }
}, function (data, response) {
    alert("ok");
}, function () {
    alert("request failed");
}, OData.batchHandler);

結果就很順利…我們可以看到POST後的網址已經正確了。

image

而Respose也正常了。

image

所以到這邊,就可以推測是這裡的問題了。

至於解法,目前小弟我查到的訊息,除了等Web API更新以外,要不就是自己重新定義處理器。

處理器的Code如下。( 此Code來源於JayData論壇的Fenderbender神人 )

public class PathFixODataBatchHandler : DefaultODataBatchHandler
        {

            public PathFixODataBatchHandler(HttpServer httpServer)
                : base(httpServer)
            {
            }

            public override async Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(IEnumerable<ODataBatchRequestItem> requests, CancellationToken cancellationToken)
            {
                if (requests == null)
                {
                    throw new System.ArgumentNullException("requests"); // Error.ArgumentNull("requests");
                }

                IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();

                try
                {
                    foreach (ODataBatchRequestItem request in requests)
                    {
                        fixRequestUri(request);
                        responses.Add(await request.SendRequestAsync(Invoker, cancellationToken));
                    }
                }
                catch
                {
                    foreach (ODataBatchResponseItem response in responses)
                    {
                        if (response != null)
                        {
                            response.Dispose();
                        }
                    }
                    throw;
                }

                return responses;
            }

            private void fixRequestUri(ODataBatchRequestItem request)
            {
                foreach (HttpRequestMessage req in ((ChangeSetRequestItem)request).Requests)
                {
                    var oldUri = req.RequestUri;
                    var newUriBuilder = new UriBuilder(oldUri);
                    newUriBuilder.Path = "/odata" + newUriBuilder.Path;
                    req.RequestUri = newUriBuilder.Uri;
                }
            }
        }

其實重點就是後面,將path加上"/odata"。

然後我們在Web API的Router Config再來調整一下,改用上面的Class。

config.Routes.MapODataRoute(
    routeName: "ODataRoute",
    routePrefix: "odata",
    model: GetModel(),
    //batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
    batchHandler: new PathFixODataBatchHandler(GlobalConfiguration.DefaultServer));
config.EnableQuerySupport();

這樣就可以了~~

後記

目前查出來的文章,都指向ASP.NET WEB API的問題XDD,因為除了JayData外,BreezeJS也有同樣的問題;但WCF下,就無此問題;但不管怎樣,有用到此技術的朋友們,可以稍微注意一下喔!!

參考網址