2013年12月24日 星期二

Common.Logging

在開始使用log4net或NLog這種Logging元件來處理log,是一個很美好的開發經驗,但在開發類別庫專案的時後,會碰到循環參考的問題,這時後改用Common.Logging會比較好

Common.Logging是一個Log介面,可以支援log4net、NLog、Enterprise Library Logging,透過設定檔把Log轉接到真正要使用的Log元件,在日後轉換Log元件的時後很方便,詳細的介紹可以參考官網,以下用一個類別庫專案搭配一個應用程式來介紹這個元件的使用方式

Common.Logging 2.2.0版之後,相依於Common.Logging.Core套件
應該是為了可儶性,把介面和抽象類別分離到Core套件
在套件的使用上要做點調整

Common Logging 2.3.1版之後,已不再使用Common.Logging.Core
為了比較好在Log套件的多個版本之間切換,對應的Log套件後面都會有指定的版本
例如Common.Logging.NLog31對應到NLog 3.1版本

首先開啟一個類別庫專案,並透過NuGet加入Common.Logging參考


簡單寫一個函式讓之後引用的程式來呼叫
using Common.Logging;

namespace ClassLibrary1
{
    public class Class1
    {
        private ILog log = LogManager.GetLogger<Class1>());

        public void SayHello()
        {
            this.log.Info("Hello");
        }
    }
}

再來新增一個Windows Form應用程式,並引用剛寫的庫別庫專案

簡單寫一個Button Click來呼叫類別庫中的函式
using System;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ClassLibrary1.Class1 x = new ClassLibrary1.Class1();
            x.SayHello();
        }
    }
}

因為要轉接到NLog去,所以再新增套件Common.Logging.NLog31,對應的NLog版本就是3.1
如果要對應到其他的版本,可以選擇對應的套件

加入NLog,為了方便起見,順便加入NLog.Configuration和NLog Schema for Intellisense(TM)

接下來透過設定檔把Log轉接到NLog

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <sectionGroup name="common">
            <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
        </sectionGroup>
    </configSections>
    <common>
        <logging>
            <factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog31">
                <arg key="configType" value="FILE" />
                <arg key="configFile" value="~/NLog.config" />
            </factoryAdapter>
        </logging>
    </common>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
                <bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>
NLog使用預設的設定

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <!-- 
  See http://nlog-project.org/wiki/Configuration_file 
  for information on customizing logging rules and outputs.
   -->
    <targets>
        <!-- add your targets here -->
        <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
                layout="${longdate} ${uppercase:${level}} ${message}" />

    </targets>

    <rules>
        <!-- add your logging rules here -->
        <logger name="*" minlevel="Trace" writeTo="f" />
    </rules>
</nlog>
輸出的結果




NuGet Multiple Framework 建置

NuGet使用一段時間之後,開始碰到共用的元件,在不同專案的Framework版本不同,會有引用上的困擾,在網路上找了幾個解決方案,實作之後個人覺的比較簡單的方法如下

Step1 每個版本建立一個方案檔
一般專案的命名為net20、net35、net40、net45...
其他類型的專案則再加上縮寫
Client Profile(client)
WindowsPhone(wp)
Silverlight(sl)
CompactFramework(cf)
更多完整的資料請參考官網文件
http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package

Step2 每一個版本建立一個專案檔



Step3 每一個專案檔選擇不同的Framework,以及條件式編譯符號和修改輸出路徑
注意每個組態都要設定喔
Step4 依Framework版本,加上條件式編譯

using System;
using System.Collections.Generic;

#if NET35 || NET40 || NET45
using System.Linq;
#endif

using System.Text;

#if NET40 || NET45
using System.Threading.Tasks;
#endif

namespace ClassLibrary1
{
    public class Class1
    {
    }
}

Step5 手動建立nuget spec檔

<?xml version="1.0"?>
<package >
  <metadata>
    <id>ClassLibrary1</id>
    <version>1.0.0</version>
    <authors>xian</authors>
    <owners>xian</owners>
    <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
    <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
    <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Package description</description>
    <releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
    <copyright>Copyright 2013</copyright>
    <tags>Tag1 Tag2</tags>
    <dependencies>
      <dependency id="SampleDependency" version="1.0" />
    </dependencies>
  </metadata>
  <files>
        <file src="ClassLibrary1\**\*.cs" exclude="ClassLibrary1\obj\**\*.cs" target="src" />
  <file src="ClassLibrary1\bin\Release\net20\ClassLibrary1.dll" target="lib\net20\ClassLibrary1.dll" />
        <file src="ClassLibrary1\bin\Release\net20\ClassLibrary1.pdb" target="lib\net20\ClassLibrary1.pdb" />
  <file src="ClassLibrary1\bin\Release\net35\ClassLibrary1.dll" target="lib\net35\ClassLibrary1.dll" />
        <file src="ClassLibrary1\bin\Release\net35\ClassLibrary1.pdb" target="lib\net35\ClassLibrary1.pdb" />
        <file src="ClassLibrary1\bin\Release\net40\ClassLibrary1.dll" target="lib\net40\ClassLibrary1.dll" />
        <file src="ClassLibrary1\bin\Release\net40\ClassLibrary1.pdb" target="lib\net40\ClassLibrary1.pdb" />
  <file src="ClassLibrary1\bin\Release\net45\ClassLibrary1.dll" target="lib\net45\ClassLibrary1.dll" />
        <file src="ClassLibrary1\bin\Release\net45\ClassLibrary1.pdb" target="lib\net45\ClassLibrary1.pdb" />
    </files>
</package> 

Step6 加上測試用批次檔

@echo off
set MSBUILD=%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe
if not exist %MSBUILD% set MSBUILD=%WINDIR%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe
if not exist %MSBUILD% set MSBUILD=%WINDIR%\Microsoft.NET\Framework\v3.5\msbuild.exe
if not exist %MSBUILD% (
 echo MSBuild not found
 exit
) 

%MSBUILD% ClassLibrary1.net20.sln /property:Configuration=Release /m
%MSBUILD% ClassLibrary1.net35.sln /property:Configuration=Release /m
%MSBUILD% ClassLibrary1.net40.sln /property:Configuration=Release /m
%MSBUILD% ClassLibrary1.net45.sln /property:Configuration=Release /m
nuget pack ClassLibrary1.nuspec
pause


瀏覽結果

2013年11月4日 星期一

好用的LogViewer

Log2Console是個好用的Log Viewer,可以配合Log4Net或NLog使用
首先到官網下載


安裝好後先設定接收方式


這裡以UDP為例



設定要使用的Port號就行了
下方是log4net的設定範例


以NLog來當日誌輸出元件的話,可以透過Chainsaw將Log寫到指定的位址和埠號
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <!-- 
  See http://nlog-project.org/wiki/Configuration_file 
  for information on customizing logging rules and outputs.
   -->
    <targets>
        <target xsi:type="Chainsaw" name="viewer" address="tcp://127.0.0.1:7071"  />
    </targets>

    <rules>
        <logger name="*" minlevel="Trace" writeTo="viewer" />
    </rules>
</nlog>


隨便輸出幾個Log試試
using NLog;

namespace ConsoleApplication1
{
    class Program
    {
        private static Logger log = LogManager.GetLogger("Program");

        static void Main(string[] args)
        {
            log.Trace("trace");
            log.Debug("debug");
            log.Info("info");
            log.Warn("warn");
            log.Error("error");
            log.Fatal("fatal");
        }
    }
}

單擊列表中的Log,可以在下方看到詳細資料
左上角的下拉選單可以用來過濾要顯示的層級
中間的搜尋框可以用來過濾日誌的內容
右邊的樹狀選單可以用來過濾Logger名稱
至於更詳細的設定,例如字型、顏色等功能,可在Settings裡面調整





2013年10月9日 星期三

TeamCity 刪除特定的Nuget Package


因為設定上的問題,從TeamCity所生成管理的Nuget package的版本號碼會有衝突
所以需要把有問題的package先刪除
但TeamCity本身沒有直接支援刪除的功能,找到了一篇用reset api刪除的方式

利用Filddler來執行rest api,網址為http://<teamcity>/httpAuth/app/rest/builds/id
最後的id為指定的建置版本,也是要刪除的版本
可以很容易的在一些設定頁面上,找一下網址後面的BuildId所帶的值來取得


使用Filddler執行DELETE方法,並在標頭中加入Authorization: Basic (UserName:Password)就行了


括號內的帳密需要用base64編碼,利用TextWizard來轉換就行了


參考連結

http://stackoverflow.com/questions/10218318/how-to-remove-a-specific-version-of-a-package-on-a-teamcity-nuget-feed

Fiddler 下載

2013年8月30日 星期五

TypeScript 安裝



TypeScript會安裝到C:\Program Files(x86)\Microsoft SDKs\TypeScript資料夾內


安裝後,VS2012會多出一個TypeScript的範本


新增檔案的時後,也會多出一個TypeScript的檔案類型


在檔案總管中,TypeScript會包含js和map檔案
建置動作會是TypeScriptCompile

在設定選項中,會有存檔編譯的選項


另外安裝Web Essentials套件會更方便,首先打開擴充功能和更新


選擇Web Essentials 2012安裝


安裝後在編輯TypeScript檔案時,會出現預覽視窗



選項中的會有更多設定


P.S. Web Essentails 套件在V3.0版本中,已經移除所有對TypeScript的支援
原因是VS2013已經內建了
如果在VS2012還要使用之前提供的功能的話,建議安裝V2.9版就好了

TypeScript 筆記


TypeScript 安裝


參考資源

TypeScript 官網

TypeScript Revealed
ISBN:978-1-4302-5725-7

2013年8月29日 星期四

Knockout.js JSON處理

ko.toJS 將ViewModel轉成JavaScript物件
ko.toJSON 將ViewModel轉成JSON格式
ko.mapping.fromJS 合併JS物件到原有的ViewModel物件
ko.mapping.fromJSON 合併JSON資料到原有的ViewModel物件

ko.mapping需另外下載,可透過nuget取得


Knockout.js 自訂範本

template是用來把複雜或是可重覆使用的輸出格式,獨立出來的一個功能
使用name來指定範本的id,data用來指定傳入範本的資料,as為資料的別名
再配合foreach、if等流程控制來輸出資料

Knockout.js 表單處理

click 觸發click事件的動作
event 繫結自訂的事件處理
submit 觸發表單送出事件的動作
enable 是否啟用
disable 是否停用
value 繫結表單內容
hasFocus 是否得到焦點
checked 是否核取
options 繫結下拉選單
optionsText為文字內容
optionsValue為下拉選單的值
selectedOptions 可複取的下拉選單所選取的資料

Knockout.js 流程控制

foreach 列舉ko.observableArray繫結的內容
if 條件判斷為true才處理該子元素內容
ifnot 反向判斷
with 指向物件參考,讓子元素內容可以直接使用該物件的屬性
而不需要使用完整的物件名稱



Knockout.js 資料繫結

visible 控制是否為可見元素
text 顯示文字內容
html 顯示html內容
css 設定css類別
style 自訂css樣式
attr 自訂html屬性

Knockout.js 簡介

Knockout.js是一套Javascript Library,主要用來處理網頁上MVVM模式的資料更新與繫結
要引用Knockout最方便的方式,不外就是透過Nuget

Scripts資料夾中就會出現Knockout的檔案
knockout-{version}.js為壓縮版本
knockout-{version}.debug.js為末壓縮版本
knockout的使用方式,首先宣告一個ViewModel類別,然後透過ko.applyBindings來繫結這個類別
除了顯示資料之外,也支援雙向繫結
以下是一個簡單的例子

2013年8月16日 星期五

HTTP Status Code

HTTP Status Code

100~199 訊息狀態碼

StatusCode ReasonPhrase 說明
100 Continue Client端有一個Message要發送給Server
但希望在發送之前先看一下Server是否會接受這個Message
所以在Request表頭中加入一個名稱Expect值為100 Conutinue
Server端收到後如果可以接受,就馬上回應這個狀態碼給Client
表示收到了Request的初始部份,要求Client端繼續
通常用於傳送接收一個大的Message時的優化
101 Switching Protocols 伺服器正在切換成Request封包表頭中
Update所指定的通訊協定

200~299 成功狀態碼

Status Code Reason Phrase 說明
200 OK 就是OK
201 Created 資源已建立,用於回應建立資源的要求,例如PUT
回應的訊息中,應該在表頭中加入Location
用來告訴Client建立好的資源URL位置
202 Accepted 請求已被接受,但伺服器還未執行任何動作,也不能保證會完成
簡單的說,就是朕知道了...
203 Non-Authoritative
Information
Header中包含的訊息,並不是來自於要求的伺服器
而是來自資源的一個副本
204 No Content 用來告訴瀏覽器,在不轉頁的情況下,刷新表單頁面
205 Reset Content 用來告訴瀏覽器,Reset目前頁面中的表單元素
206 Partial Content Client透過Request Header中的Range來要求部份或某個範圍的資源
這個狀態碼就是成功執行了一個部份或是某個範圍的請求
Response中的Header必須包含Content-Range、Date
以及ETag或是Content-Location

300~399 重轉向定位狀態碼

Status Code Reason Phrase 說明
300 Multiple Choices 訊息類狀態碼
301 Moved Permanently 所要求的資源已移除,永久轉向
Response Header中的Location為新的URL
之前的要求都要改成新的URL
HTTP/1.0版本使用
302 Found 和301狀態碼類似
差別在於Header中Location給出的URL是暫時性的
之後的要求還是要用原來的URL
303 See Other 回應Client端另一個URL來獲得資源
主要目的是允許POST請求的回應轉向到某個資源
304 Not Modified 通過Client的Request Header來判斷
如果資源末被修改的話,就回應這個狀態碼
305 Use Proxy 必須通過一個代理來訪問資源
代理的位置由Header中的Location給出
306 尚未使用 該狀態碼目前尚未使用
307 Temporary Redirect 和301狀態碼類似
差別在於Header中Location給出的URL是暫時性的
之後的要求還是要用原來的URL
HTTP/1.1版本使用

400~499 客戶端錯誤狀態碼

Status Code Reason Phrase 說明
400 Bad Request 告訴客戶端它發送了一個錯誤的要求
401 Unauthorized 告訴客戶端在要求資源之前,需要先經過身份驗證
402 Payment Required 目前尚未使用
403 Forbidden 告訴客戶端服務已被拒絕了
拒絕的原因可在回應的主體部份來描述
但這個狀態碼通常是不想說明拒絕原因的時後使用的
404 Not Found 找不到所要求的資源
405 Method Not Allowed 告訴客戶端收到不支援的HTTP Method
並在回應的表頭中以Allow告知可支援的Method
406 Not Acceptable Client端可在表頭中用Accept告之可接受的資源型態
當Server端沒有所指定的資源型態時,用這個狀態碼來告之
通常會在回應的表頭中,加上一些訊息
讓Client端知道為什麼要求無法滿足
407 Proxy Authentication Required 和401狀態碼類似
但目標是要求對資源進行認證的代理伺服器
408 Request Timeout 完成要求的時間太長,伺服器所回應的狀態碼
並關閉連接
逾時時間以伺服器設定為主
409 Conflict 和訴客戶端,資源發出衝突
410 Gone 和404類似
只是伺服器曾經擁有該資源,只是被移除了
411 Length Required 告訴客戶端表頭中需要包含Content-Length
412 Preconditaion Failed 客戶端發起條件請求,其中一個條件失敗了的時後使用的回應碼
413 Request Entity Too Large 客戶端發送的Message比伺服器能夠處理的要大時
用這個狀態碼回應
414 Request URI Too Long 客戶端發送的URL比伺服器能夠處理的長度要長時
要這個狀態碼回應
415 Unsupported Media Type 伺服器無法支援客戶端發送的mime type時
用這個狀態碼回應
416 Requested Range Not Satisfiable 客戶端要求的表頭Range中指定了資源的某個範圍時
伺服器在範圍無效或是無法滿足時,用這個狀態碼回應
417 Expectation Failed 客戶端要求的表頭Expect中包含了一個期望時
伺服器無法滿足時,用這個狀態碼回應

500~599 伺服器端錯誤狀態碼

Status Code Reason Phrase 說明
500 Internal Server Error 伺服器錯誤
501 Not Implemented 伺服器無法處理Client所發起的要求
例如使用了不支援的HTTP Method
502 Bad Gateway 代理或是Gateway收到了一條偽裝的回應
就會回應這個狀態碼
503 Service Unavailable 伺服器目前無法提供服務,例如過於忙錄
可以在Response Header中加入一個Retry-After
用來表示服務什麼時後會變為可用的
504 Gateway Timeout 和狀態碼408類似,只是這個的回應來自於一個Gateway或代理
它們在等待另一個伺服器對其請求時逾時了
505 HTTP Version Not Supported 伺服器收到不支援的通訊協定版本時的狀態回應碼

HTTP Method

HTTP Method

GET 這是最常用的方法,通常用在請求伺服器上的某個資源
該方法沒有Body主體,可以被Cache
HTTP protocols中並沒有規範長度限制,有限制的是瀏覽器和Web Server
不同瀏覽器中的限制也不同,IE為2048,FF為8192
POST 向伺服器輸入數據,通常會以透過表單的方式
資料會在HTTP Body的部份,以name=value的方式表示,分隔符號為&
該方法不會被Cache
PUT 將資源寫入伺服器端,如果資源已存在,就用這個資源來取代原有的資源
因為需要對伺服器寫入,所以基本上都需要先通過身份驗證是否有寫入的權限
寫入成功後,應回應201,並在表頭中用Location告訴Client端寫入的資源位置
DELETE 要求伺服器刪除指定的資源
PATCH 要求伺服器更新指定的資源
HEAD 和GET方法的行為很類似,但在伺服器端回應的訊息中,只包含HTTP Header而已
並不會返回HTTP Body的部份,適用於在不取得資源的情況下,了解資源的狀態
TRACE 要求伺服器返回接收到的表頭資料
用意是用來追蹤發送出去的表頭,和到伺服器接收到的表頭,有何差異
OPTIONS 要求伺服器返回可接收的HTTP Method指令有那些
因為並不是所有Method都有被Web Server所實作

2013年8月1日 星期四

jQuery 陣列處理

.each(collection, callback) 迭代陣列元素
.grep(array, callback, [invert]) 過濾陣列
第三個參數用來設定傳回符合條件或是不符合條件的元素
.map(array, callback) 將陣列元素處理後回傳成新的陣列
.merge(array1, array2) 將array2合併到array1陣列中
.unique(array) 將陣列中重覆的項目移除

jQuery 字串處理

.trim() 字串去空白
.param() 字串序例化

jQuery Ajax呼叫方式

.ajax({})為底層呼叫方式,參數如下
.ajaxSetup()則是用來設定參數的預設值

url 請求的URL
type 請求的方式,例如POST或是GET
data 請求的參數
dataType 指定回應的格式,有以下幾種格式
xml, html, json, jsonp, script, text
timeout 逾時的毫秒數,如果超出設定的時間,就會觸發error設定的callback函式
global 設定是否要觸發全域事件處理,啟用為true, 停用為false
預設是啟用狀態
contentType 指定請求的內容類型
預設為application/x-www-form-urlencoded,和表單發送的類型一樣
success 請求成功時呼叫的函式
error 請求失敗時呼叫的函式
complete 整個請求完成時呼叫的函式
beforeSend 發出請求之前呼叫的函式
可以用來執行請求前的操作,例如自訂標頭
urlasync 非同步為true, 同步為false
預設是非同步
processData 是否對傳送的資料做URL編碼,true為編碼,false為不編碼
預設是會編碼
ifModified 根據標頭的Last-Modified來判斷是否更新
預設為不檢查標頭

jQuery Ajax快捷方法

jsfiddle的echo網址,大部份都需要用POST方式才會有反應
所以例子中會有幾個方式沒有反應

.get() 發出Get要求
.post() 發出Post要求
.load() 以Get方式載入指定的網址
.getJSON() 以Get方式取得JSON資料
.getScript() 以Get方式取得Script指令

jQuery 全域事件處理

jQuery v1.8之後,ajax全域事件只能綁定到document元素

.ajaxStart() ajax要求開始
.ajaxSend(event, jqXHR, ajaxOptions) 發出ajax要求
.ajaxSuccess(event, jqXHR, ajaxOptions, ThrownError) ajax要求成功的話就觸發這個事件
.ajaxError(event, XMLHttpRequest, ajaxOptions) ajax要求失敗的話就觸發這個事件
.ajaxComplete(event, XMLHttpRequest, ajaxOptions) ajax要求完成
.ajaxStop() ajax要求結束

2013年7月30日 星期二

jQuery 樣式表操作

.addClass() 增加樣式類別
.removeClass() 移除樣式類別
.hasClass() 是否有指定的樣式類別
.toogleClass() 切換樣式類別
.css() 取得或設定指定的樣式

jQuery 屬性操作

.attr() 取得或設定屬性
.removeAttr() 移除屬性
.prop() 取得或設定屬性
.removeProp() 移除屬性

.attr()和.prop()的差異在1.6版本
對於bool值的屬性,例如checked, disabled, readonly, selected...,用prop會比較合適

2013年7月29日 星期一

jQuery 移除元素

.detach() 移除符合指定的選擇器元素,包含選擇器本身
被移除的元素原本繫結的事件,還原回去後並不會消失
.empty() 移除符合指定的選擇器元素,不包含選擇器本身
被移除的元素原本繫結的事件,還原回去後並不會消失
.remove() 移除符合指定的選擇器元素,包含選擇器本身
被移除的元素原本繫結的事件,還原回去後會消失
.unwrap() 移除符合指定的選擇器外層元素

jQuery 替換元素

.html() 在符合指定的選擇器元素內容中,取得或設定HTML結構
.text() 在符合指定的選擇器元素內容中,取得或設定純文字內容
.replaceWith() 將符合指定的選擇器元素,用指定的HTML結構取代
.replaceAll() 和replaceWith的效果一樣,差別在於主要的處理對象是新建的新元素
可以做串接的函式處理後,再取代符合的選擇器元素

jQuery 附加元素

.append() 將HTML結構插入到符合指定的選擇器內容的尾端
.appendTo() 和append的效果一樣,差別在於主要的處理對象是新建的新元素
可以做串接的函式處理後,再插入符合的選擇器內容尾端
.prepend() 將HTML結構插入的符合指定的選擇器內容的前端
.prependTo() 和prepend的效果一樣,差別在於主要的處理對象是新建的新元素
可以做串接的函式處理後,再插入符合的選擇器內容前端

jQuery 插入元素

.after() 將指定的元素插入每一個符合的選擇器後面
.insertAfter() 和after的效果一樣,差別在於主要的處理對象是新建的新元素
可以做串接的函式處理後,再插入符合的選擇器後面
.before() 將指定的元素插入每一個符合的選擇器前面
.insertBefore() 和before的效果一樣,差別在於主要的對象是新建的新元素
可以做串接的函式處理後,再插入符合的選擇器前面

jQuery 包裝元素

.wrap() 每一個符合指定的選擇器元素都被封裝到指定的HTML結構中
.wrapAll() 將所有符合選擇器的元素封裝在一個指定的HTML結構中
.wrapInner() 每一個符合指定的選擇器元素內部都封裝一個指定的HTML結構

2013年7月25日 星期四

jQuery 滑鼠事件

.click() 在元素上單擊左鍵
.dblclick() 在元素上雙擊左鍵
.hover() 進入和離開元素時觸發
.mousedown() 在元素上按下滑鼠按鍵
.mouseenter() 進入元素時觸發
.mouseleave() 離開元素時觸發
.mousemove() 在元素上移動時觸發
.mouseout() 離開元素時觸發
支持Bubble Up
.mouseover() 進入元素時觸發
支持Bubble Up
.mouseup() 在元素上放開滑鼠按鍵

jQuery 鍵盤事件

.keydown() 按下鍵盤
.keypress() 壓住鍵盤
.keyup() 放開鍵盤

jQuery 事件繫結

.bind() 為一個元素綁定一個事件處理
執行多次會重覆綁定
動態生成元素綁定事件會有先後問題
.unbind() 從元素上移除一個之前綁定的事件處理
.live() 附加一個事件處理器到指定選擇器的元素上
version deprecated:1.7, remove:1.9
.die() 從元素移除之前用.live()綁定的事件
version deprecated:1.7, remove:1.9
.delegate() 指派事件處理
利用事件的Bubble Up特性,在上層元素掛上事件處理
V1.4.2以上適用
.undelegate() 解除事件指派
V1.4.2以上適用
.on() 繫結事件
v1.7以上適用
.off() 解除事件
v1.7以上適用
.trigger() 觸發事件
.one() 在元素上綁定一個事件處理函式
該處理函式只會執行一次,然後刪除自已

2013年7月19日 星期五

jQuery 自定義效果

.animate() 自訂動畫效果
.queue() 動畫效果只能影響數值效果
如要在動畫中改變非數值效果需放入佇列中執行
.dequeue() 繼續執行佇列之後的效果
.clearQueue() 移除佇列
.delay() 延遲效果
.finish() 結束動畫效果
.stop() 停止動畫效果

2013年7月15日 星期一

jQuery 滑動效果

.slideDown() 滑動顯示效果
.slideUp() 滑動隱藏效果
.slideToggle() 切換滑動顯示隱藏效果

jQuery 漸變效果

.fadeIn() 淡入效果
.fadeOut() 淡出效果
.fadeTo(duration, opacity) 切換透明度
.fadeToggle() 切換淡出淡入效果

jQuery 基礎效果

.hide() 隱藏元素
.show() 顯示元素
.toggle() 切換顯示隱藏元素

jQuery 內容過濾語法

內容過濾語法
範例 說明
:contains(foo) 包含foo文字的元素,區分大小寫
:empty 選取沒有子節點的元素
:has(E) 選取有E為子元素的元素
:parent 選取有子節點的元素
:hidden 選取隱藏元素
:visible 選取可見元素

jQuery 表單元素語法

表單元素語法
範例 說明
:button 任何按鈕,包含button、submit、reset
:checkbox 核取方塊元素
:checked 已核取的核取方塊或單選鈕
:disabled 已停用的表單元素
:enabled 可啟用的表單元素
:focus 按鈕
:file 選取所有檔案元素
:image 選取所有圖片元素
:input 選取所有輸入元素,包含text、select、textarea、button
:password 選取密碼元素
:radio 選取單選鈕元素
:reset 選取重置按鈕
:selected 選取被選取的選項
:submit 選取發送按鈕
:text 選取文字元素

jQuery 子元素過濾語法

子元素過濾語法
範例 說明
:first-child 第一個子元素
:first-of-type 同級元素之間的第一個相同的元素
:last-child 最後一個子元素
:last-of-type 同級元素之間的最後一個相同的元素
:nth-child(n) 第n個子元素且為指定的元素
:nth-last-child(n) 倒數第n個子元素
:nth-last-of-type(n) 倒數第n個指定的子元素
:nth-of-type(n) 第n個指定的子元素
:only-child 只有一個子節點的元素
:only-of-type 同級元素之間只有一個子節點的元素

2013年7月12日 星期五

jQuery 位置過濾語法

位置過濾語法,從一個位置索引從0開始計算
範例 說明
:first 第一個元素
:last 最後一個元素
:odd 奇數元素
:even 偶數元素
:eq(n) 第n個元素
:gt(n) 大於第n個元素
:lt(n) 小於第n個元素
:not() 反向選擇
:header h1~h6標題元素
:animated 正在執行動畫效果的元素

jQuery 屬性選擇器

屬性選擇器
範例 說明
$("E[A]") 具有A屬性的元素E
$("E[A='V']") 具有A屬性的元素E,且值為V
$("E[A^='V']") 具有A屬性的元素E,且值開頭為V
$("E[A$='V']") 具有A屬性的元素E,且值結尾為V
$("E[A*='V']") 具有A屬性的元素E,且值內容包含V
$("E[A!=V]") 具有A屬性的元素E,且值內容不等於V
$("E[A|='V']") 具有A屬性的元素E,且值內容的前綴為V或是V-
$("E[A~='V']") 具有A屬性的元素E,且用空格分隔的值中有V
$("E[A1='V1'][A2='V2'][A3='V3']...") 具有A屬性的元素E,且屬性符號多重串接

jQuery 層級選擇器

層級選擇器
範例 說明
$("E F") 後代選擇器,找出在E元素的所有子元素裡面的所有F元素
遞迴子元素的子元素,效率較差
$("E>F") 子代選擇器,找出在E元素的第一層子元素中的所有F元素
只找子元素,效能較佳
$("E+F") 毗鄰選擇器,E元素之後同層級的第一個F元素
$("E~F") 鄰後選擇器,E元素之後同層級的所有F元素

jQuery 基本選擇器

基本選擇器
範例 說明
$("*") 選取所有元素
("#id") 符合id名稱的元素,因為id是唯一值,所以效能會比較好
$(".C") 符合CSS類別名稱C的所有元素
$("E") 符合E標籤的所有元素

2013年7月2日 星期二

2013年7月1日 星期一

Json.Net 日期處理

Json.Net 序列化的日期格式,在4.5版之前是Microsoft Date Format:"\/Date(ticks)\/"
4.5版之後,預設的日期格式已經改成ISO 8601 standard
可以透過JsonSerializerSettings的方式來指定序列化的格式
反序列化的時後會自動識別不需另外指定

另外還有一種JavaScript格式,需要透過指定JavaScriptDateTimeConverter來轉換
反序列化的時後也需要明確指定,否則會出現錯誤

DateTime d = DateTime.Now;

Console.WriteLine("datetime:{0}\r\n", d);

// ISO8601的格式
string jsonString1 = JsonConvert.SerializeObject(d);
Console.WriteLine("ISO8601 Date Format:{0}", jsonString1);
DateTime d1 = JsonConvert.DeserializeObject<DateTime>(jsonString1);
Console.WriteLine("datetime:{0}, kind:{1}\r\n", d1, d1.Kind);

// Microsoft Date Format
string jsonString2 = JsonConvert.SerializeObject(
    d, 
    new JsonSerializerSettings() 
    {
        DateFormatHandling = DateFormatHandling.MicrosoftDateFormat 
    });
Console.WriteLine("Microsoft Date Format:{0}", jsonString2);
DateTime d2 = JsonConvert.DeserializeObject<DateTime>(jsonString2);
Console.WriteLine("datetime:{0}, kind:{1}\r\n", d2, d2.Kind);

// JavaScript Date Format
string jsonString3 = JsonConvert.SerializeObject(d, new JavaScriptDateTimeConverter());
Console.WriteLine("JavaScript Date Format:{0}", jsonString3);
DateTime d3 = JsonConvert.DeserializeObject<DateTime>(jsonString3, new JavaScriptDateTimeConverter());
Console.WriteLine("datetime:{0}, kind:{1}\r\n", d3.ToLocalTime(), d3.Kind);

Console.ReadLine();

執行結果

ISO8601格式需要ECMAScript 5才能支援,瀏覽器的版本可以參考ECMAScript 5 compatibility table
打開一個Chrome的主控台並輸入以下幾行指令
// 建立一個ISO8601格式的日期資料
d = {"today":"2013-07-01T15:21:47.6748196+08:00"};
// 日期會被當成字串
d.today;
// 直接使用new Date得到日期
new Date(d.today)

執行結果

2013年6月27日 星期四

Json.Net 轉換IDictionary 介面

Json.Net 處理 IDictionary介面的方式,會解析成物件的key和value
以下是一個簡單的例子

// 淮備資料
Dictionary<string, string> dict1 = new Dictionary<string, string>()
{
    {"a", "1"},
    {"b", "2"},
    {"c", "3"},
};

// 序列化
string jsonString = JsonConvert.SerializeObject(dict1);
Console.WriteLine(jsonString);

// 反序列化
Dictionary<string, string> dict2 = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
foreach (KeyValuePair<string, string> item in dict2)
{
    Console.WriteLine("key:{0}, value:{1}", item.Key, item.Value);
}

Console.ReadLine();

執行結果

Json.Net 轉換 IEnumerable 介面

Json.Net對於IEnumerable介面的處理,同樣是序列化成陣列的格式
首先先宣告個物件以便之後的處理

private class Person
{
    public int id { get; set; }
    public string name { get; set; }
    public DateTime date { get; set; }
}

轉換IEnumerable介面,以List為例
static void Main(string[] args)
{
    // 淮備資料
    List<Person> list1 = new List<Person>()
    {
        new Person()
        {
            id = 1,
            name = "p1",
            date = DateTime.Today.AddDays(-1)

        },
        new Person()
        {
            id = 2,
            name="p2",
            date = DateTime.Today
        },
        new Person()
        {
            id = 3,
            name = "p3",
            date = DateTime.Today.AddDays(1)
        }
    };

    // 序列化
    string jsonString = JsonConvert.SerializeObject(list1);
    Console.WriteLine(jsonString);

    // 反序列化
    List<Person> list2 = JsonConvert.DeserializeObject<List<Person>>(jsonString);
    foreach (var item in list2)
    {
        Console.WriteLine("id:{0}, name:{1}, date:{2}", item.id, item.name, item.date);
    }

    Console.ReadLine();
}

執行結果

Json.Net 用法

首先用最基本的JsonTextWriter和JsonTextReader
JsonTextWriter使用成對的方法來輸出Json字串
JsonTextReader使用Read方法來持續讀取Json字串
看得出來在使用上有點拖泥帶水
static void Main(string[] args)
{
    string jsonString = string.Empty;

    using (StringWriter sw = new StringWriter())
    {
        using (JsonTextWriter writer = new JsonTextWriter(sw))
        {
            // 開始輸出物件
            writer.WriteStartObject();

            // 輸出屬性:id
            writer.WritePropertyName("id");
            writer.WriteValue(1);

            // 輸出屬性:name
            writer.WritePropertyName("name");
            writer.WriteValue("xian");

            // 輸出屬性today
            writer.WritePropertyName("today");
            writer.WriteValue(DateTime.Today);

            // 開始輸出陣列
            writer.WritePropertyName("arraydata");
            writer.WriteStartArray();
            writer.WriteValue(1);
            writer.WriteValue(2);
            writer.WriteValue(3);

            // 結束輸出陣列
            writer.WriteEndArray();

            // 結束串出物件
            writer.WriteEndObject();
        }

        jsonString = sw.ToString();
        Console.WriteLine(jsonString);
    }

    using (StringReader sr = new StringReader(jsonString))
    {
        using (JsonTextReader reader = new JsonTextReader(sr))
        {
            while (reader.Read())
            {
                if (reader.Value != null)
                {
                    Console.WriteLine("token:{0}, value:{1}", reader.TokenType, reader.Value);
                }
                else
                {
                    Console.WriteLine("token:{0}", reader.TokenType);
                }
            }
        }
    }

    Console.ReadLine();
}

執行結果

使用JObject可以簡化一點
// 淮備Json資料
JObject obj1 = new JObject()
{
    new JProperty("id", 1),
    new JProperty("name", "xian"),
    new JProperty("today", DateTime.Today),
    new JProperty("arraydata", 
    new JArray()
    {
        new JValue(1),
        new JValue(2),
        new JValue(3),
    })
};

string jsonString = obj1.ToString();
Console.WriteLine(jsonString);

// 解析Json格式
JObject obj2 = JObject.Parse(jsonString);
Console.WriteLine(
    "id:{0}, name:{1}, today:{2}, arraydata:{3}",
    obj2["id"],
    obj2["name"],
    obj2["today"],
    obj2["arraydata"]);
Console.ReadLine();
執行結果

最常使用還是JsonConvert這一個方式,用法也很直覺

// 淮備資料
Person p1 = new Person()
{
    id = 1,
    name = "xian",
    today = DateTime.Today
};

// 序列化物件
string jsonString = JsonConvert.SerializeObject(p1);
Console.WriteLine("jsonstring:{0}", jsonString);

// 反序列化物件
Person p2 = JsonConvert.DeserializeObject<Person>(jsonString);
Console.WriteLine("id:{0}, name:{1}, today:{2}", p2.id, p2.name, p2.today);

Console.ReadLine();
執行結果

Json.Net 介紹

Json.Net 是一個第三方套件,其強大的功能和.Net內建的比較表,可以去官網看一下
個人比較有感覺的差別是
  • 支援.Net 2.0
  • 支援LINQ  
  • 支援暱名類別
  • 支援dynamic物件
  • 日期格式為ISO8601
該專案為Open Source,可以到codeplex下載原始碼
安裝的方式也很簡單,打開NuGet輸入Json.Net就行了
Json.Net也有完整的線上文件可以參考

以下是Json.Net常用的物件

JsonConvert 最容易使用的一個靜態工具類別
JsonTextReader 讀取Json格式
JsonTextWriter 輸出Json格式
JObject 對應Json物件,就是大括號包起來的部份
JArray 對應Json陣列,就是中括號包起來的部份
JValue 對應Json值的部份

2013年6月20日 星期四

DataContractJsonSerializer 日期處理

使用DataContractJsonSerializer序列化日期時,格式為\/Date(ticks+時區資料)\/這個樣子
因為有時區資料,所以不用像JavaScriptSerializer一樣還要手動加上ToLocalTime函式
// 初始化DataContractJsonSerializer類別
DateTime d1 = DateTime.Now;
DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(DateTime));
string jsonString = string.Empty;

// 序列化資料
using (MemoryStream ms = new MemoryStream())
{
    dcjs.WriteObject(ms, d1);
    jsonString = Encoding.UTF8.GetString(ms.ToArray());
    Console.WriteLine(jsonString);
    Console.WriteLine();
}

// 反序列化資料
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
{
    DateTime d2 = (DateTime)dcjs.ReadObject(ms);
    Console.WriteLine("d1:{0}, kind:{1}", d1, d1.Kind);
    Console.WriteLine("d2:{0}, kind:{1}", d2, d2.Kind);
}

Console.ReadLine();

執行結果

使用JavaScript處理的時後,因為多了時區資料,所以reg要修改一下
打開一個Chrome的主控台並輸入以下幾行指令
// 建立一個JSON格式的資料
data = { "today": "\/Date(1371716206301+0800)\/"};

// 看一下資料解析的樣子
data.today;
 
// 把前後的斜線去掉的樣子
data.today.replace(/\//g, "");
 
// 使用eval來得到日期
eval("new " + data.today.replace(/\//g, ""))
 
// 使用reg取出ticks的部份
data.today.replace(/\/Date\((.*?)\)\//g, "$1");
 
// parseInt轉成數字後也能得到日期
new Date(parseInt(data.today.replace(/\/Date\((.*?)\)\//g, "$1")))
執行結果如下

DataContractJsonSerializer 轉換 IDictionary 介面

轉換IDictionary介面的方式也一樣,只要注意初始化DataContractJsonSerializer類別時傳入IDictionary介面就好
但是輸出的結果並不如預期中為物件的格式
直接宣告一個Dictionary來當範例
// 淮備序列化的類別資料
Dictionary<int, object> dataList = new Dictionary<int, object>()
{
    {1, "a"},
    {2, "b"},
    {3, "c"}
};

初始化時要傳入這個Dictionary的型別
// 初始化DataContractJsonSerializer類別
DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(Dictionary<int, object>));
string jsonString = string.Empty;

序列化的方式一樣
// 序列化資料
using (MemoryStream ms = new MemoryStream())
{
    dcjs.WriteObject(ms, dataList);
    jsonString = Encoding.UTF8.GetString(ms.ToArray());
    Console.WriteLine(jsonString);
    Console.WriteLine();
}

反序列化的方式也一樣
// 反序列化資料
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
{
    Dictionary<int, object> data2List = dcjs.ReadObject(ms) as Dictionary<int, object>;
    foreach (KeyValuePair<int, object> item in data2List)
    {
        Console.WriteLine(
            "Key:{0}, Value:{1}",
            item.Key,
            item.Value);
    }
}

執行結果可以看到序列化成JSON字串時,會固定變成Key:xx, Value:oo的陣列
這在不同JSON元件之間的轉換會造成一些問題

DataContractJsonSerializer 轉換 IEnumerable 介面

轉換IEnumerable的用法和一般的資料沒什麼不同
差別只在於初始化類別的時後,傳入的資料型別也要改成IEnumerable
宣告一個相同的資料型別來做範例
[DataContract]
public class JsonData
{
    [DataMember(Name = "ID", Order = 0)]
    public int Id { get; set; }

    [DataMember(Order = 1)]
    public string Name { get; set; }

    [DataMember(Order = 2)]
    public DateTime Today { get; set; }

    [DataMember(Order=3)]
    public bool IsBool { get; set; }

    [IgnoreDataMember()]
    public string UnlessField { get; set; }
}

準備一個List來當做序列化的資料
初始化DataContractJsonSerializer類別的時後,也要傳入這個List的型別
// 淮備序列化的類別資料
List<JsonData> dataList = new List<JsonData>()
{
    new JsonData()
    {
        Id = 1,
        Name = "data1",
        Today = DateTime.Now.AddDays(-1),
        IsBool = true,
        UnlessField = "沒用到的欄位1"
    },
    new JsonData()
    {
        Id = 2,
        Name = "data2",
        Today = DateTime.Now,
        IsBool = false,
        UnlessField = "沒用到的欄位2"
    },
    new JsonData()
    {
        Id = 3,
        Name = "data3",
        Today = DateTime.Now.AddDays(1),
        IsBool = true,
        UnlessField = "沒用到的欄位3"
    }

};

// 初始化DataContractJsonSerializer類別
DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(List<JsonData>));
string jsonString = string.Empty;

序列化的方法一樣
// 序列化資料
using (MemoryStream ms = new MemoryStream())
{
    dcjs.WriteObject(ms, dataList);
    jsonString = Encoding.UTF8.GetString(ms.ToArray());
    Console.WriteLine(jsonString);
    Console.WriteLine();
}

反序列化的方法也一樣
// 反序列化資料
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
{
    List<JsonData> data2List = dcjs.ReadObject(ms) as List<JsonData>;
    foreach (JsonData item in data2List)
    {
        Console.WriteLine(
            "Id:{0}, Name:{1}, Today:{2}, IsBool:{3}, UnlessField:{4}",
            item.Id,
            item.Name,
            item.Today,
            item.IsBool,
            item.UnlessField);
    }
}

執行結果資料一樣序列化成陣列的樣子,反序列化也沒什麼問題

DataContractJsonSerializer 用法

首先需要先定義資料類別
DataContract用來標記類別
DataMember用來標記屬性,Name可以用來變更序列化後的名稱,Order則是序列化的順序
IgnoreDataMember用來標記屬性,表示不參與序列化過程
[DataContract]
public class JsonData
{
    [DataMember(Name = "ID", Order = 0)]
    public int Id { get; set; }

    [DataMember(Order = 1)]
    public string Name { get; set; }

    [DataMember(Order = 2)]
    public DateTime Today { get; set; }

    [DataMember(Order=3)]
    public bool IsBool { get; set; }

    [IgnoreDataMember()]
    public string UnlessField { get; set; }
}

初始化DataContractJsonSerializer類別時,需傳入要序列化的資料型別
// 淮備序列化的類別資料
JsonData d1 = new JsonData()
{
    Id = 1,
    Name = "data",
    Today = DateTime.Now,
    IsBool = true,
    UnlessField = "沒用到的欄位"
};

// 初始化DataContractJsonSerializer類別
DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(JsonData));
string jsonString = string.Empty;

序列化資料使用WriteObject寫入Stream
再用Encoding取得json字串
// 序列化資料
using (MemoryStream ms = new MemoryStream())
{
    dcjs.WriteObject(ms, d1);
    jsonString = Encoding.UTF8.GetString(ms.ToArray());
    Console.WriteLine(jsonString);
}

反序列化資料先用Encoding取得取得Byte Array讀入Stream
再用ReadObject讀出資料型別
// 反序列化資料
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
{
    JsonData d2 = dcjs.ReadObject(ms) as JsonData;
    Console.WriteLine(
        "Id:{0}, Name:{1}, Today:{2}, IsBool:{3}, UnlessField:{4}",
        d2.Id,
        d2.Name,
        d2.Today,
        d2.IsBool,
        d2.UnlessField);
}

執行結果如下
1. 日期的部份加入了時區的資料,所以反序列化回來後不用再手動加上ToLocalTime函數
2.  標記為IgnoreDataMember的欄位並沒有參考序列化的過程,反序列化回來後是空字串

DataContractJsonSerializer 介紹

JavaScriptSerializer是.NET 3.5中新增的類別
該類別在System.Runtime.Serialization.Json命名空間下面
需引用System.Runtime.Serialization.dll
建構式
DataContractJsonSerializer(Type) 傳入要轉換的資料型別
DataContractJsonSerializer(Type, DataContractJsonSerializerSettings) 傳入要轉換的資料型別,和設定值

方法
WriteObject
將物件序列化成JSON字串
ReadObject
將JSON字串反序列化成指定的物件

2013年6月19日 星期三

JavaScriptSerializer ScriptIgnoreAttribute

ScriptIgnore這個Attribute是用來取消屬性或欄位的序列化

using System;
using System.Web.Script.Serialization;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string jsonString = string.Empty;
            Data data = new Data()
            {
                Id = 1,
                Name = "data"
            };

            JavaScriptSerializer jss = new JavaScriptSerializer();
            jsonString = jss.Serialize(data);
            Console.WriteLine(jsonString);
            Console.ReadLine();
        }

        private class Data
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    }
}

執行結果如下

加上ScriptIgnore這個Attribute之後
using System;
using System.Web.Script.Serialization;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string jsonString = string.Empty;
            Data data = new Data()
            {
                Id = 1,
                Name = "data"
            };

            JavaScriptSerializer jss = new JavaScriptSerializer();
            jsonString = jss.Serialize(data);
            Console.WriteLine(jsonString);
            Console.ReadLine();
        }

        private class Data
        {
            public int Id { get; set; }

            [ScriptIgnore]
            public string Name { get; set; }
        }
    }
}
執行結果如下