2014年12月19日 星期五

升級 Microsoft Azure Storage 4.3.0 以節省輸出流量、加快速度

使用 Microsoft Azure Storage 來擷取 Azure Table 的資料,通常要考慮速度與流量的最佳化,但原本的版本是 Microsoft Azure Storage 2.0.6,是沒有支援 OData 的,且 SDK 又是以 URL 去和 Azure Table 要資料,且一次都會要所有欄位資料,且速度也很慢,更別說還要做一些資料處理了。

我舉我的 Log 為例:

Log 資料欄位約略有 10 欄。
TableQuery query = (new TableQuery());
var data = table.ExecuteQuery(query);

var _query = (
    from entity in data
    // where entity.ErrorMessage != ""
    where entity.Timestamp > DateTime.Now.AddDays(-1)

    // where entity.Location.EndsWith("PUT")
    orderby entity.Timestamp descending
    select entity
);
DateTime std = DateTime.Now;

var r = _query.ToList();

DateTime etd = DateTime.Now;
TimeSpan diffDT = etd.Subtract(std);
Console.WriteLine("DateTime Method : {0} ms", diffDT.TotalMilliseconds);

資料有 370 筆,執行時間 25275.3736 ms,約 25 秒。

使用 Fiddler 來看:


由於 Azure Table 資料輸出有限制筆數,所以才會要那麼多次,每一份資料都 760000 bytes 左右,0.76 MB 左右。

升級 Microsoft Azure Storage 4.3.0 後,將程式碼稍微改一下,抓出需要使用的欄位就好:
TableQuery query = (new TableQuery()).Select(new string[] { "partitionKey", "Timestamp" });

資料有 330 筆,執行時間 9779.4132 ms,約 9.7 秒。

使用 Fiddler 來看:

這樣一來,既省輸出流量,也縮短時間,所以還沒有升級不訪升級試看看。



2014年11月25日 星期二

ASP.NET MVC 解決加入「控制器」( Controller ) 消失的問題

前陣子將專案升級,在 NuGet 安裝 ASP.NET Web API 2.2 for OData v4.0 套件,產生許多 dll 版本錯誤之問題。

修正後,又發現在要加入「控制項」( Controller ) 時,找不到該選項,如下圖:


甚至連加入「區域」( Areas ) 也找不到。

後來找到 visual studio 2013 'add controller' missing 文章解說,照以下方法即可解決:

1.

卸載專案

2.

編輯專案檔

3.

尋找 ProjectTypeGuids 標籤 ( 請先備分 ),將內容改為
<ProjectTypeGuids>{E3E379DF-F4C6-4180-9B81-6769533ABE47};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

儲存

4.

重新載入專案,完成。





2014年8月8日 星期五

ASP .NET MVC4 WebApi - 不允許路由(Route)跨區域(Area)找Controller

目前專案設定權限是採用Area進行區分,基本規劃為角色 1..n Area

最近在強化權限控管,發現在Area找不到Controller的時候,竟然會去其他的Area找有沒有符合名稱的Controller(如何切換 Area 的 Web Api 路由)

對規劃的權限區分來說,這一個非常嚴重的問題


舉個例子:
(角色=>Area)
人員管理者=>People
薪資管理者=>Salary

王小明有人員管理者這個角色,所以他可以使用People裡的所有Controller, 碰巧王小明知道了Salary的某隻Api路徑可以取得全公司員工的薪資(api/Salary/AllPeople), 就權限管理機制,王小明沒有Salary的Area權限,所以被判定為無權限(401)。

到這邊都很沒問題,一切都很美好...如果這樣就結束,還要這篇幹嘛

偏偏王小明很有求知的精神,想說只要是People就會被放行,那如果把Salary改成People不知道可不可以?
於是原本的路徑(api/Salary/AllPeople)變成了(api/People/AllPeople)。

這時候權限機制判斷王小明是人員管理者,可以訪問People的Area,所以放行讓他通過, 殊不知在People下沒有AllPeople的Controller,但對權限機制來說並不重要,有沒有是ControllerSelector的問題。

這時候AreaHttpControllerSelector出馬了,經過一連串的檢查,發現People下沒有AllPeople的Controller,沒辦法走下去,那就交給老爸(DefaultHttpControllerSelector)去處理吧。

而老爸是不管這些Area的,只要符合ControllerName就好,所以找到了Salary的AllPeople,然後就跟小弟(ApiControllerActionSelector)說,從Salary的AllPeople找符合的Action來執行吧。

接著就會發現王小明引發美麗錯誤的結果,他拿到不該拿的資料了...其實也還好


這樣是不行的,如果Route有接到area的話,就只能從指定的Area下找Controller,沒找到就丟錯誤出去,所以修改一下AreaHttpControllerSelector
...略...
        private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
        {
            var controllerName = base.GetControllerName(request);

            var areaName = GetAreaName(request);
            if (string.IsNullOrEmpty(areaName))
            {
                return null;
            }

            var type = GetControllerTypeByArea(areaName, controllerName);
            if (type == null)
            {
                //原本回傳null是為了向老爸求救,今天兒子要獨當一面了
                //return null;
                throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound,
                    string.Format("No route providing a controller name was found to match request URI \"{0}\"", new object[] { request.RequestUri })));
            }

            return new HttpControllerDescriptor(_configuration, controllerName, type);
        }


參考:
如何切換 Area 的 Web Api 路由
DefaultHttpControllerSelector.cs Source Code
ApiControllerActionSelector.cs Source Code