2014-03-09

理解 PredicateBuilder 實作方式

PredicateBuilder 提供了以 OR 串接 bool 的 Lambda Expression,使用上會像下面的程式:
var predicate = PredicateBuilder.False<Product>();

predicate = predicate.Or(p => p.Name == 'Shoe');
predicate = predicate.Or(p => p.Price > 100);


它的原始碼如下:
public static Expression<Func<T, bool>> Or<T> (
    this Expression<Func<T, bool>> expr1,
         Expression<Func<T, bool>> expr2
)
{
    var invokedExpr = Expression.Invoke (
        expr2, 
        expr1.Parameters.Cast<Expression>()
    );
 
    return Expression.Lambda<Func<T, bool>>(
        Expression.OrElse (expr1.Body, invokedExpr), 
        expr1.Parameters
    );
}


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


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

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

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

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


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

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

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

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

但以執行狀態跟操作行為作一個歸納,可以區分以下幾種回傳結果:
資料操作 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 接收資訊的範例
$.ajax({
    type: "POST",
    url: document.location,
    success: function (data, textStatus, jqXHR) {
        alert(data);
    },
    error: function (jqXHR, textStatus, errorThrown) {
        alert(jqXHR.responseText);
    }
});


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


C# MVC 傳遞錯誤訊息的範例
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = 400;
return Content("儲存失敗!!");

CSS 選擇器權重表

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

Ex:
.title {} /* 1 */

div span.title {} /* 21 */

#product-list .title {} /* 101 */

使用 Stream 讀取 cUrl 下載結果

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

$url = 'http://www.google.com.tw';

/* 建立接收的 Temp File Stream */
$tmpfile = tmpfile();


$curl = curl_init();

/* 指定下載的的 URL */
curl_setopt($curl, CURLOPT_URL, $url); 

/* 指定存放的 File Stream */
curl_setopt($curl, CURLOPT_FILE, $tmpfile);

/* 執行並取得狀態 */
$status = curl_exec($curl);
curl_close($curl);

if(!$status){ 
    fclose($tmpfile); 
    exit('error'); 
}


/* Temp File Stream 指標歸零 */
fseek($tmpfile, 0);

/*一次讀取一行*/
while (($line = fgets($tmpfile)) !== false) {
    var_dump($line);
}

/* 關閉 Stream */
fclose($tmpfile);

使用 stream 讀取指令列結果

$cmd = "find ./ -path './*/*/*'";

/* 執行指令,並取得 stream */
$fp = popen($cmd, "r");

/*一次讀取一行*/
while (($filePath = fgets($fp)) !== false) {
    $filePath = trim($filePath);
    var_dump($filePath);
}

/* 關閉 stream */
pclose($fp);