2014-03-09 17:47

理解 PredicateBuilder 實作方式

PredicateBuilder 提供了以 OR 串接 bool 的 Lambda Expression,使用上會像下面的程式:
  1. var predicate = PredicateBuilder.False<Product>(); 
  2.  
  3. predicate = predicate.Or(p => p.Name == 'Shoe'); 
  4. predicate = predicate.Or(p => p.Price > 100); 


它的原始碼如下:
  1. public static Expression<Func<T, bool>> Or<T> ( 
  2.    this Expression<Func<T, bool>> expr1, 
  3.         Expression<Func<T, bool>> expr2 
  4. ) 
  5. { 
  6.    var invokedExpr = Expression.Invoke ( 
  7.        expr2,  
  8.        expr1.Parameters.Cast<Expression>() 
  9.    ); 
  10.  
  11.    return Expression.Lambda<Func<T, bool>>( 
  12.        Expression.OrElse (expr1.Body, invokedExpr),  
  13.        expr1.Parameters 
  14.    ); 
  15. } 


上面的程式雖然不長,但實在有點小難懂,搞不懂他為什麼要這麼寫??讓我們逐步解釋吧!


假設 expr1 是 x => x.Name == 'Shoe'
假設 expr2 是 y => y.Price > 100

  1. var invokedExpr = Expression.Invoke( 
  2.    expr2,  
  3.    expr1.Parameters.Cast<Expression>() 
  4. ); 
先組合出( 利用 expr1 的參數去執行 expr2 )的表示式 invokedExpr
產生出了語法會像 (y => y.Price > 100) (x)

  1. Expression.OrElse(expr1.Body, invokedExpr) 
將 expr1.Body 與 invokedExpr 以 || 運算子組合起來
產生出了語法會像 x.Name == 'Shoe' || (y => y.Price > 100) (x)

  1. Expression.Lambda<Func<T, bool>>( 
  2.    Expression.OrElse(expr1.Body, invokedExpr),  
  3.    expr1.Parameters 
  4. ); 
將剛剛組合好的 || 運算表示式再組合成 Lambda 表示式
產生出了語法會像 x => (x.Name == 'Shoe' || (y => y.Price > 100) (x))


參考來源:
Dynamically Composing Expression Predicates
2014-03-05 23:38

利用 HTTP Status Codes 傳遞 Ajax 成功失敗的狀態

一般處理 Ajax 回應時會傳送的資訊種類有:資料、成功訊息、錯誤訊息、失敗訊息以及處理狀態,傳遞的資訊種類並不一致,再加上除了資料之外,通常還希望能傳遞處理狀態,這種情況大部分會選擇是以 JSON 的方式傳遞這兩個訊息,以下是常見的幾種格式:

  1. { code: 1, msg: "OK" } 
  2. { success: true, result: "data", errorMsg: "" } 
  3. { status: 'success', result: [], errorMsg: "" } 
  4. //... 

但以執行狀態跟操作行為作一個歸納,可以區分以下幾種回傳結果:
資料操作 HTTP Method 成功 錯誤/失敗
檢視(Read) GET 資料 錯誤/失敗訊息
新增(Create)
修改(Update)
刪除(Delete)
POST 成功訊息 錯誤/失敗訊息

從上面的歸納可以看出規律性,接著只要有方法可以傳送處理的狀態,以及能夠區分資料的種類,其實就單純很多,而 HTTP Status Codes 就是用來傳遞 HTTP 的處理狀態,如果利用這個方式來傳遞自訂的處理狀態,這樣 HTTP Content 就可以很單純傳遞資料,讓資料格式不受限於 JSON,還可以使用其他格式(text, xml, html),而且 XMLHttpRequest 本身就有處理 HTTP Status Codes 的能力,而 jQuery.ajax 也有提供 error status 的處理,所以可以利用這個來定義狀態的處理,在 HTTP Status Codes 有幾個已經定義狀態,很適合用來回傳處理狀態的資訊:

400 Bad Request 錯誤的請求 適用在表單內容的錯誤,如必填欄位未填、Email 格式錯誤
403 Forbidden 沒有權限,被禁止的 適用在沒有登入或權限不足
500 Internal Server Error 內部服務器錯誤 適用在程式的錯誤


jQuery 接收資訊的範例
  1. $.ajax({ 
  2.    type: "POST", 
  3.    url: document.location, 
  4.    success: function (data, textStatus, jqXHR) { 
  5.        alert(data); 
  6.    }, 
  7.    error: function (jqXHR, textStatus, errorThrown) { 
  8.        alert(jqXHR.responseText); 
  9.    } 
  10. }); 


PHP 傳遞錯誤訊息的範例
  1. if (php_sapi_name() == 'cgi'){ 
  2.    header("Status: 400 Bad Request"); 
  3. }else{ 
  4.    header("HTTP/1.0 400 Bad Request"); 
  5. } 
  6. exit("儲存失敗!!"); 


C# MVC 傳遞錯誤訊息的範例
  1. Response.TrySkipIisCustomErrors = true; 
  2. Response.StatusCode = 400; 
  3. return Content("儲存失敗!!"); 
2014-03-05 15:18

CSS 選擇器權重表

!important style="" #id htmlTag .class
權重 10000 1000 100 10 1

Ex:
  1. .title {} /* 1 */ 
  2.  
  3. div span.title {} /* 21 */ 
  4.  
  5. #product-list .title {} /* 101 */ 

2014-03-05 14:44

使用 Stream 讀取 cUrl 下載結果

使用 stream 的好處就是用多少拿多少,不會因為資料大小而占用大量的記憶體。

  1. $url = 'http://www.google.com.tw'; 
  2.  
  3. /* 建立接收的 Temp File Stream */ 
  4. $tmpfile = tmpfile(); 
  5.  
  6.  
  7. $curl = curl_init(); 
  8.  
  9. /* 指定下載的的 URL */ 
  10. curl_setopt($curl, CURLOPT_URL, $url);  
  11.  
  12. /* 指定存放的 File Stream */ 
  13. curl_setopt($curl, CURLOPT_FILE, $tmpfile); 
  14.  
  15. /* 執行並取得狀態 */ 
  16. $status = curl_exec($curl); 
  17. curl_close($curl); 
  18.  
  19. if(!$status){  
  20.    fclose($tmpfile);  
  21.    exit('error');  
  22. } 
  23.  
  24.  
  25. /* Temp File Stream 指標歸零 */ 
  26. fseek($tmpfile, 0); 
  27.  
  28. /*一次讀取一行*/ 
  29. while (($line = fgets($tmpfile)) !== false) { 
  30.    var_dump($line); 
  31. } 
  32.  
  33. /* 關閉 Stream */ 
  34. fclose($tmpfile); 
2014-03-05 14:30

使用 stream 讀取指令列結果

  1. $cmd = "find ./ -path './*/*/*'"; 
  2.  
  3. /* 執行指令,並取得 stream */ 
  4. $fp = popen($cmd, "r"); 
  5.  
  6. /*一次讀取一行*/ 
  7. while (($filePath = fgets($fp)) !== false) { 
  8.    $filePath = trim($filePath); 
  9.    var_dump($filePath); 
  10. } 
  11.  
  12. /* 關閉 stream */ 
  13. pclose($fp);