網頁

ASP.NET 2.0 Study web site and .NET Framework SDK



以上兩個都是比較早期.NET Framework 2.0的資料,還可以下載離線安裝正體中文版:Microsoft .NET Framework 2.0 軟體開發套件 (SDK) (x86)Microsoft .NET Framework 2.0 軟體開發套件 (SDK) (x64),但安裝它有個問題,它使用的是MS SQL Server 2005 Express資料庫,重點是它的執行個體名稱「SQLEXPRESS」無法改變,這會讓後續在安裝Visual Studio 2008 / 2010或MS SQL Server 2008 (R2) Express有點困擾。

在入門知識了解差不多後,一些新電腦上我就沒有在安裝它們了,但如果你剛入門學ASP.NET,不要管是.NET Framework 2.0/3.5/4.0,我非常推薦安裝它,好好的去K它,它每一篇短短小小的,很容於吸收,是學ASP.NET入門上不可多得的教材。

如果你像我一樣,一開始就把Visual Studio 2008/ 2010 and MS SQL Server 2008 (R2)(Express)安裝好了,而且都是採用預設值,那你上面的SDK大概就裝不起來了(MS SQL Server執行個體名稱會衝突到),也不太可能再去移除Visual Studio 2008/ 2010 and MS SQL Server 2008 (R2)(Express),那可是大工程,比安裝還幸苦很多。去看看Microsoft .NET Framework SDK QuickStart Tutorials,原汁原味,我印象中我有看過Microsoft放過線上正體中文版SDK,但不知為何原因被移除了。

那有沒有Microsoft .NET Framework 4.0?有的,不過在Microsoft的中文downloads網站找不到,因為只有出英文版,而且與Windows / Windows 7 SDK整合在一起:「Microsoft Windows SDK for Windows 7 and .NET Framework 4 (ISO)」(Only English Version),跟前面的Microsoft .NET Framework 2.0 SDK比較起來,這一份SDK就比較像原味的MSDN,不適合入門者看,合適進階者或查詢時使用。

你可以買書、上補習班或自修,但不管如何,那份Microsoft .NET Framework 2.0 SDK適合每一個剛入門ASP.NET的人來看,基礎觀念最重要,把基礎打好才會有未來。不要急著玩什麼雲不雲?什麼Lin什麼q?E什麼F?

沒有好的基礎,你會被開槍…打到趴。

從頭開始,一步一步往上爬,最實在。^_^

Reference:

T-SQL -- Common Table Expression (CTE) 教學重點筆記

開始使用SQL Server 2005後所新增的語法來改寫T-SQL,我真的喜歡它們。如果你已經有安裝MS SQL Server 2005 / 2008 / 2008 R2…等版本,那一定不要錯過在SQL Server 2005所新增的Common Table Expression(一般資料表運算式, CTE)。

CTE我就不寫教學了,我是看這一篇「一般資料表運算式」學的,看一下語法看一下教學,基本使用上是沒有問題的,以下是一些重點筆記:

  • 可以把CTE當成"Temp View"
  • 如果在Store Procedure使用CTE,會造成每次執行都會re-compiler,效能低落
  • 資料集未以Table存放於Database時,適合用CTE
  • View通常用來分解大型查詢,如果只用一次,那View不是好辦法
  • 子查詢只能從所在的"陳述式"存取,如果有多個相同的子查詢,必須含重覆程式碼,難閱讀及維護
  • CTE能在同批次下,重覆使用(像View一樣)
  • CTE只能用於其後第一個陳述式(Select, Insert, Delete, Update),此陳述式可多次取用CTE
  • CTE後面可接另一個CTE,使用逗號(,)

遞迴CTE


  1. 建立傳回最上層的查詢(Anchor Member)
  2. 撰寫遞迴查詢(Recursive Member)
  3. Union All
  4. 確認不會傳回的資料列
 因為是遞迴,所以有兩點須注意:

  1. 你必須明確終止"子句條件"
  2. 或由CTE協助終止
    1. Recursive Member傳回"零"記錄
    2. 設定 MaxRecursion
    3. CTE預設最大遞迴數為 100
  3. Recursive Member只能參考CTE一次
;With RecurCTE(c1, c2, c3, ...cn) As 
(
  -- Anchor Member Query
  Union All
  -- Recursive Member Query
)
Select * From RecurCTE
Option (MaxRecursion 10) -- 最大遞迴數 10

遞迴CTE限制

遞迴CTE不能使用於:
  • Select Distinct
  • Group by
  • Having
  • Top
  • Left/Right Outer Join

相信我,把CTE學起來,會對你在T-SQL的撰寫上有很大的幫助。

Reference:

Tools -- LINQPad雲端版

LINQ是從.NET Framework 3.5所提的一個存取資料庫的新技術,而且以非常快速的速度走紅,走紅自有它的道理。不過,你在學習LINQ的過程式中一定會有Debug的需求,LINQ到底轉換成什麼T-SQL?但在如Visual Studio 2010裡,也很難Debug,暗黑執行緒有寫過一篇「在ASP.NET中觀察LINQ to SQL所產生的T-SQL語法」,但每次都要重新寫那些Code然後執行,不太可能。

那學習LINQ的人又一定聽過或使用過超強的LINQPad工具,它就是解決你在學習LINQ上的好工具,直接連線到資料庫,然後測試語法,即時查詢LINQ所產生的T-SQL…都是Visual Studio裡所欠缺的。

LINQPad軟體小小的,不管你是要安裝或免安裝版本,它都有提供,而且現在更提供LINQPad雲端版,目前只提供Google Chrome版本,連線後必須下載一個RoozzSetup.exe程式,安裝後,剛才的「LINQPad雲端版」就會跑出與軟體版一模一樣的UI畫面,執行起來效果也不差,真的是太強了。

重點是


雖後是雲端版,但只要資料庫認證資料正確,一切連線都是沒有問題的但也發現一個小問題,它會儲存你的狀態資料,所以像資料庫連線等…訊息,記得,在公用、非個人電腦上,使用完要記得刪除。

ps.. 「LINQPad雲端版」是我自己稱呼的。^_^
ps.. 我在Mozilla Firefox執行也是正常的,但IE就會提示未支援。

Reference:

T-SQL -- Update加強版之Write子句修改大數值資料類型(MAX)

在MS SQL Server 2005新增了「MAX」的資料類型,如:varchar(MAX)、nvarchar(MAX)、varbinary(MAX),MSDN翻譯為「大數值資料類型」,基本上是可以儲存到2^31-1位元組的資料,而且加強了數種功能:

  1. 在triger中,可以在inserted和deleted資料表中使用大數值資料類型;
  2. Update新增Write()函數,可以對大數值資料類型執行區塊更新;
我在「透過資料庫上傳下載檔案」裡提過一次,我再提一次:

在未來的 Microsoft SQL Server 版本中,將移除 ntext、text 和 image 等資料類型。請避免在新的開發工作中使用這些資料類型,並規劃修改目前在使用這些資料類型的應用程式。請改用 nvarchar(max)、varchar(max) 和 varbinary(max)
如果你使用MS SQL Server 2005之後的版本,請注意以上訊息。

Update之Write()子句


我們來看Update含Write()子句的語法:
Update 資料表
Set 資料行 = .Write(運算式, 起點位置,長度)
From 來源資料表
Where 條件

看實例:

-- 暫存資料表
CREATE TABLE #t1
(
 id INT IDENTITY(1,1) NOT NULL,
 CONTENT NVARCHAR(MAX)
);
GO

-- 新增兩筆資料,也是使用SQL Server 2005的新語法
INSERT #t1 VALUES 
(N'在MS SQL Server 2005新增了「MAX」的資料類型,如:varchar(MAX)、nvarchar(MAX)、varbinary(MAX),MSDN翻譯為「大數值資料類型」,基本上是可以儲存到2^31-1位元組的資料,而且加強了數種功能'), 
(N'在未來的 Microsoft SQL Server 版本中,將移除 ntext、text 和 image  等資料類型。請避免在新的開發工作中使用這些資料類型,並規劃修改目前在使用這些資料類型的應用程式。請改用  nvarchar(max)、varchar(max) 和 varbinary(max)。')
GO

-- 將id=1的MS換成Microsoft
-- 起點位置由0開始計算
UPDATE #t1
SET CONTENT.write('Microsoft',1,2)
WHERE id=1

-- 將id=2的Microsoft換成MS
UPDATE #t1
SET CONTENT.write('MS',5,9)
WHERE id=2
GO

SELECT  *
FROM    #t1
GO

DROP TABLE #t1
GO


使用Write()子句只有一點點不方便,就是你必須自己計算「起點位置」及「長度」,還有不能更新值為Null等。

Reference:

SSMS -- SQL Server Management Studio加強工具之SSMS Tools Pack

如果你覺得SSMS只是拿來打打T-SQL,那你就錯了。那如果你覺得SSMS在UI或操作上有那麼一點不人性,那你跟我一樣。SSMS用久了,總覺得有點那麼小怪怪,例如說,每次都要重覆打Select * From Table,就沒有一個快速鍵按下後,自動產生樣版,雖然可以叫出那超大樣版集合的「範本總管」,但比較起Visual Studio 2010的人性化,SSMS總是差那麼一點。

不過,我找到了一個工具,可以拿來加強SSMS,它是由SQL MVP所開發的「SSMS Tools Pack」,小小不到 2MB,但它已經支援到 MS SQL Server 2011版(嚇, @_@),細部的功能網頁Features都有介紹:

  • SQL Snippets
    例如,在輸入區打上「ssf」後按下「Enter」或「Tab」就產生預設的代碼,如「SELECT * FROM 」。
  • Window Connection Coloring
  • Window Content History, Query Execution History and Current Window History
    你的執行歷史和當前視窗的歷史,這在除錯時很有用。
  • Format SQL
    格式化視窗,連大小寫都會幫你轉換好
  • Search Table, View or Database Data
    直覺的視窗搜尋功能,不用為了找資料而下一堆T-SQL指令。
  • Run one script on multiple databases
  • Copy execution plan bitmaps to clipboard or file
    直接複製執行計畫的圖形到剪貼簿或檔案。
  • Search Results in Grid Mode
    在執行結果的表格中搜尋。
  • Generate Insert statements from resultsets, tables or databases
    直接幫你產生表格或資料庫的Insert語法檔案。
  • Regions and Debug sections
    收合及除錯區塊的標示。
  • Running custom scripts from Object Explorer
    從物件總管執行自訂的指令碼。
  • CRUD (Create, Read, Update, Delete) stored procedure generation
    產生CRUD預儲程序。
  • New query template
  • General Options

在安裝過程中,你能看到它完整支援MS SQL Server 2005/2008/2011,尤其它的SQL Snippets,我愛死它了,簡單好用,可以讓我減少打字且提高正確性,還可以建立自己常用的快速鍵及樣版。

我幫各位整理一份內建快速鍵對應表,可放在手邊查詢,常用的幾次就記起來了:



按I <tab | enter>就出現Insert,按rwn <tab | enter>就出現「ROW_NUMBER() OVER(PARTITION BY <> ORDER BY <> )」挖~挖~挖~,我超感動的。

看完有沒有很心動,快下載安裝,保證不後悔。

Reference:

T-SQL -- Insert加強版之資料表值建構函式(Values)

我們在處理一些從網頁裡來新增要求時,不免要進行T-SQL字串組合,Insert沒什麼技巧,反正組合起來的Insert及Values裡帶入的資料類型正確,大約就沒什麼問題。

一般都是單行、單行的新增,也就是一筆一筆的新增,這是針對前台而言,但對於管理的後台,很常有大量新增資料的需求,這時候必須組合出相對應的Insert語法,然後再傳送給MS SQL Server進行資料新增動作,但這裡如果有錯,通常很難Debug,因為如果有100筆的大量新增,就是同時有100行程式碼要看,如果有500筆就是500行程式碼要看,實在很傷眼。

例如Northwind.sql中的程式碼:

-- Customers
INSERT "Customers" VALUES('ALFKI','Alfreds Futterkiste','Maria Anders','Sales Representative','Obere Str. 57','Berlin',NULL,'12209','Germany','030-0074321','030-0076545')
INSERT "Customers" VALUES('ANATR','Ana Trujillo Emparedados y helados','Ana Trujillo','Owner','Avda. de la Constitución 2222','México D.F.',NULL,'05021','Mexico','(5) 555-4729','(5) 555-3745')
INSERT "Customers" VALUES('ANTON','Antonio Moreno Taquería','Antonio Moreno','Owner','Mataderos  2312','México D.F.',NULL,'05023','Mexico','(5) 555-3932',NULL)
INSERT "Customers" VALUES('AROUT','Around the Horn','Thomas Hardy','Sales Representative','120 Hanover Sq.','London',NULL,'WA1 1DP','UK','(171) 555-7788','(171) 555-6750')

這種Code要你從100行或500行裡找出問題,或是從前端(*.aspx)組合出來的Code,要人命。不過這種情況到了MS SQL Server 2008之後有了改善,MS SQL Server 2008之後支援了一個「資料表值建構函式」,看起來文言文,但用起來是相當容易:

INSERT "Customers" VALUES
('ALFKI','Alfreds Futterkiste','Maria Anders','Sales Representative','Obere Str. 57','Berlin',NULL,'12209','Germany','030-0074321','030-0076545'),
('ANATR','Ana Trujillo Emparedados y helados','Ana Trujillo','Owner','Avda. de la Constitución 2222','México D.F.',NULL,'05021','Mexico','(5) 555-4729','(5) 555-3745'),
('ANTON','Antonio Moreno Taquería','Antonio Moreno','Owner','Mataderos  2312','México D.F.',NULL,'05023','Mexico','(5) 555-3932',NULL),
('AROUT','Around the Horn','Thomas Hardy','Sales Representative','120 Hanover Sq.','London',NULL,'WA1 1DP','UK','(171) 555-7788','(171) 555-6750')

有沒有看出差異?

Insert 資料表
Values (第一筆資料), (第二筆資料), (第三筆資料), ...n

我就不用每一筆資料就必須寫一行Insert,在Values之後可以接最大1000筆的資料。這樣簡化Insert ... Values ...語法之後,不論是字串的組合上,或是後續的除錯上都可減輕工作量。

重點整理:
  • SQL Server 2008後才有支援。
  • Values後的資料列,以括號括住,並以逗點隔開。
  • Values後的資料列,最大1000筆,超過部分(1001筆)必須建立新的Insert來執行。

Reference:

眼見為實?

首先,我要跟故事中小孩說一聲非常非常大的「對不起」。

婆婆除了生產、做月子外,除非特別有事,不然星期日一定會去教會,這是他們的信仰,她尊重我,我尊重她。我拿香拜拜,她阿門,但宗教一直以來都沒在我們倆之間產生任何的問題,不管是我們倆還是我們的家庭之間,這是很難得。

她在我們家,除了不能拿香外,其他的事她都會幫忙,而我家人也很開明,從知道她是「阿門」一族的第一天起,從來沒有人跟我提什麼宗教問題。而我,只要有空及我想,也是會跟婆婆去教會,婆婆會問我想去嗎?但從不強迫,我想去就去,我不想去就不去,隨我高興。

那我的小孩呢?由她們決定。

未成年,或是說未受洗之前,她們必須拿香拜拜。如果長大後,她們決定跟婆婆的信仰,那是她們的決定,我會尊重。

現在看起來,大寶貝很喜歡去教會,因為有一堆的小朋友及玩具。不過每次在唱「三疊阿門」時,她是所有大小朋友裡會放下所有事物,然後很認識的合起雙手、雙眼一起唱阿門的人。有時帶她去廟裡拜拜,我會說:「合起雙手拜拜,」等她拜完後說:「要說什麼?」她會很認真的說:「阿門!」。中毒太深@_@!

就在一次,前一晚我有點失眠,就跟今晚一樣,隔天的脾氣通常會比較不穩定些。這天這堂(這間教會早上有三堂)孩童室裡人不多,通常男生比較皮,會想盡辦法搞亂一切,不過孩童室都有保母在,所以我通常也不太理小孩的事,除了大寶貝跟著做什麼不好的行為,我會制止,其他的小孩,因為保母都沒出聲了,我也懶得理。

不過今天有位小男生不是很乖,除了頑皮外,還會做動手去推小朋友之類的動作,保母…真的是專業呀。輕輕的說、輕輕的走過去,誰誰誰,你不可以這樣哦!但這位小孩真是很…被我制止了一次、二次…我是重口氣+好言。

第三次,他把一位比他小的小朋友整個壓在地上,很用力的壓,看到,我的火山爆發了,忍無可忍,我制止他、嚇他,還有一些些不好的接觸。這個小男孩終於忍不著哭著找奶奶。是的,她在找奶奶,我心想應該又是被溺愛…保母看了也嚇到。禮拜結束後,保母很好心當起我和他爸媽的防火牆。

事後,我被婆婆念了一下,我知道不應該,但…有點無奈。我只能回應,下次只要是不碰我女兒,我都不理。

隔週,因為等不到車位,所以婆婆就先跟大寶貝去孩童室,隨後上去之後,發現婆婆在跟保母對不起,我知道是在說上週發生的事,我不想聽,所以就離遠一些跟大寶貝一起玩。

禮拜結束離開之後,婆婆跟我說,你知道為什麼那位小男生那麼皮嗎?我當然不知道。婆婆說,保母跟她說那位小男生是「過動兒」,有些行為是他自己也控制不了。

什麼是事實?

他很皮是事實。
他會動手推人是事實。
他會動手打人是事實。
他會動手壓人是事實。

這是我眼見的事實。所以我忍無可忍。

BQB,問題背後的問題。那事實背後的事實呢?

看清楚一件事不是想像中那樣簡單,原來我還是那麼膚淺,人生,還有很多功課要做,加油,Bruce,對不起,過動兒小男生,也謝謝你讓我學到這一課。

Reference:

T-SQL -- 查詢MS SQL Server所有Database 的I/O統計資料

以下程式碼改寫自胡百敬老師的Blog,因為它Blog上的T-SQL Code我Copy/Paste執行會出現,我修正了一下,順便寫點註解:

create table #t(
DbName sysname
,FileName SysName
,database_id smallint --資料庫的識別碼
,file_id smallint --檔案的識別碼
,sample_ms int --自電腦啟動之後的毫秒數。這個資料行可用來比較這個函數的不同輸出
,num_of_reads bigint --對檔案發出的讀取數
,num_of_bytes_read bigint --這個檔案讀取的總位元組數
,io_stall_read_ms bigint --使用者等候在檔案發出讀取的總時間 (以毫秒為單位)
,num_of_writes bigint --這個檔案所進行的寫入數
,num_of_bytes_written bigint --寫入檔案的總位元組數
,io_stall_write_ms bigint --使用者等候檔案完成寫入的總時間 (以毫秒為單位)
,io_stall bigint --使用者等候檔案完成 I/O 的總時間 (以毫秒為單位)
,size_on_disk_bytes bigint --該檔案在磁碟上所用的位元組數。如果是疏鬆檔案,這個數字就是資料庫快照集在磁碟上所用的實際位元組數
,file_handle varbinary(8) --這個檔案的 Windows 檔案控制代碼
);
exec sp_MSforeachdb @command1="use [?];"

INSERT #t SELECT * FROM (
SELECT DB_NAME(database_id) DBName,FILE_NAME(file_id) FileName,* FROM sys.dm_io_virtual_file_stats(DB_ID(N'?'), NULL)) t;

SELECT * FROM #t order by DbName;
drop table #t

  1. #t是暫存表格
  2. exec sp_MSforeachdb @command1="use [?];"
    此sp名稱很好猜意思,for each db,會去scan每個db,參數@command1帶入一個"use [?];",即sp_MSforeachdb會幫你做use master;use model; use msdb;…依資料庫id進行切換。
  3. Insert #t Select * From (),將Select結果寫入暫存表,重點在sys.dm_io_virtual_file_stats()的參數DB_ID(N'?'),N是Unicode名命都能正常執行,'?'會依sp_MSforeachdb每一次執行的結果被替代。如果此次是use master,那此次的?就會被替代為DB_ID(N'master')。
  4.  可以試著執行「SELECT * FROM sys.dm_io_virtual_file_stats(DB_ID(N'Northwind'), Null);
    GO」以了解此系統函數。
  5. 最後,取出結果,刪除暫存表格#t。

Reference:

JSON之教學筆記

就最近寫了很多Ajax的東西,接觸到Ajax的第一件事就是HttpXML.send()之後,伺服器要回傳什麼資料,一般都是responseText或responseXML,也就是說,1. 回傳純文字;2. 回傳XML,純文字難以分析處理(要跑DOM時),XML在伺服器端要花很多力氣去「組合」出XML資料,然後用戶端再寫很多Code去分析處理,這是很沒效率的。

所以就快快把JSON學一學吧!

JSON概念


進入JSON前,請先看我這篇「第二篇:簡單介紹JavaScript內建物件」Array物件第一段「Array宣告」的地方,宣告一個Javascript Array物件,基本上是兩種語法:

// 正式宣告
var team1 = new Array('Bruce', 'Sherry', 'Happy');
// 整合宣告
var team2 = ['Bruce', 'Sherry', 'Happy'];
alert(team1[0]); // Bruce
alert(team2[2]); // Happy

一個比較正式,一個是比較簡寫,通常Javascript寫久了,會採用第二種方式來宣告Array。

再來看Javascript物件的宣告,基本上也是兩種:

// 正式宣告
var Bruce = new Object();
Bruce.name = 'King Kong';
Bruce.age = 18;
Bruce.sex = 'male';
alert(Bruce.age); // 18

// 整合宣告
var Bruce = {
  'name' : 'King Kong',
  'age' : 18,
  'sex' : 'male'
}
alert(Bruce.name); // King Kong
alert(Bruce[name]); // King Kong

一樣,久了也是會選第二種來寫,再來我們看看Javascript的「Array + Object」的宣告:

// 宣告一個familys Array,裡面包含兩個Object
var familys = [
  {'name' : 'Bruce',
   'age' : 18,
   'sex' : 'male'},
  {'name' : 'Sherry',
   'age' : 16,
   'sex' : 'famale'}
];
alert(family[0].name); // Bruce
alert(family[1].sex);  // famale

到這裡你已經會了JSON的80%以上了,JSON (JavaScript Object Notation)已經說明的很清楚了,它是一個JavaScript的子集,它利用Object與Array來表示資料,讓資料很容易的可以交換使用。

JSON寫法


我們先來看完整的JSON寫法:

{
  'familys' = [
    {'name' : 'Bruce',
     'age' : 18,
     'sex' : 'male'},
    {'name' : 'Sherry',
     'age' : 16,
     'sex' : 'famale'}
  ]
}

JSON會建構出兩種結構:(1)「"名稱" : 值」的集合;(2)Array。

JSON的細節

比對上面範例,你會發現一點也不難。

JSON Object:
  1. 以"{"開始,以"}"結尾
  2. 每個名稱後跟著一個":"
  3. 每對"名稱:值"之間用","分隔

{ // 以"{"開始
  'name' : 'Bruce',  // 每個名稱後跟著一個":"
  'age' : 18,        // 每對"名稱:值"之間用","分隔
  'sex' : 'male'
} // 以"}"結尾

JSON Array:
  1. 以"["開始,以"]"結尾
  2. 值之間使用","

{
  // familys為一維陣列,陣列裡包含兩筆物件資料
  'familys' = [  // 以"["開始
    {'name' : 'Bruce',
     'age' : 18,
     'sex' : 'male'},  // 值之間使用","
    {'name' : 'Sherry',
     'age' : 16,
     'sex' : 'famale'}
  ] // 以"]"結尾
}

JSON Value:
  • 值本身可以是String、Number、true、false、null、ObjectArray

JSON String:
  • 由雙引號包圍的任意Unicode字元集合。可以使用"反斜線(\)"來轉義。

{ 
  "details" : "這是JSON的值. \n 此格式比XML合適Ajax交換資料使用."
}

JSON Number:
  • 與一般數值相同,除8 / 16進制外。

JSON的使用

那JSON要如何使用呢?

原生的JSON格式資料,目前在IE7以上及MF 3以上,已經內建解析JSON格式的能力,但在相容性及網路上的不確定性(其他瀏覽器),建議在有需要使用JSON格式的頁面引用json2.js,依官方說法,請不要在使用json.js這個版本的Script了。

我們有一JSON格式的String:

var jsonData = "{'familys'=[{'name' : 'Bruce', 'age' : 18, 'sex' : 'male'},
                            {'name' : 'Sherry','age' : 16, 'sex' : 'famale'}]}"

方法一:使用eval()
這個方法會引發安全性問題,我就不介紹了。

方法二:使用json2.js (IE7以上及MF 3以上可以不引用)

先在網頁中引用json2.js,然後使用json2所提供的parse方法:

var jsonData = "{'familys'=[{'name' : 'Bruce', 'age' : 18, 'sex' : 'male'},
                            {'name' : 'Sherry','age' : 16, 'sex' : 'famale'}]}"

var jsonObj = JSON.parse(jsonData);  // 將JSON格式資料轉為物件

alert(jsonObj.familys[0].name); // Bruce
alert(jsonObj.familys[1].age); // 16

這樣之後,我們就能在Ajax函式將伺服器傳回的JSON格式做解析:

// ...
if(xmlHttp.readyState == 4 && xmlHttp.status == 200){
  var jsonObj = JSON.parse(xmlHttp.responseText);
// ...
}

使用了JSON來交換資料後,你會發現相關Ajax的程式碼變簡單了,例如:

// 原始Ajax函數
function Do(xmlHttp) {
  if(xmlHttp.readyState == 4 && xmlHttp.status == 200){
    var familys = xmlHttp.responseXML.getElementsByTagName('familys');
    for (var i = 0; i < familys.length; i++) {
      var name = familys.getElementsByTagName('name')[0].firstChild.nodeValue;
      var age = familys.getElementsByTagName('age')[0].firstChild.nodeValue;
      var sex = familys.getElementsByTagName('sex')[0].firstChild.nodeValue;
    }
  }
}

// 使用JSON後的Ajax函數
function Do(xmlHttp) {
  if(xmlHttp.readyState == 4 && xmlHttp.status == 200){
    var jsonObj = JSON.parse(xmlHttp.responseText);
    var name = jsonObj.familys[0].name;
    var age = jsonObj.familys[0].age;
    var sex = jsonObj.familys[0].sex;
  }
}

不只有變簡單,而且程式的撰寫更直覺。另外,你也可以把Object轉為JSON:

// 宣告一個familys Array,裡面包含兩個Object
var familys = [
  {'name' : 'Bruce',
   'age' : 18,
   'sex' : 'male'},
  {'name' : 'Sherry',
   'age' : 16,
   'sex' : 'famale'}
];

var jsonData = JSON.stringify(familys);

更快速的是使用jQuery的$getJSON,但這超出了JSON的討論了。以上是接收解析JSON,那如果我要傳送JSON給伺服器呢?也簡單:

var xhr = request();  // 建立XHR物件
xhr.onreadystatechange = handler;
xhr.open('POST',URL);
xhr.setRequestHeader('Content-Type', 'application/json');  // 這裡是重點
xhr.send(json);

記得使用'application/json'來傳送即可,但一般伺服器端是看不懂JSON,不過沒關係,在JSON官網最下方或上網找一下,已經有很多高手分享出來可以在伺服器解析JSON的元件或Source Code,讓JSON資料傳送到伺服器後,依然方便好用。

使用JSONP進行跨站請求

這個主題比較生硬,如果你不會使用到跨網站請求,可以先跳過。

一般而言,在網站中是不太充許你去存取其他網站的內容,但不是百分百,例如iframe或img就是很好的例子,但其他HTML、Javascript就都不太能做這種事,例如XMLHttpRequest,這是一種保護。但現今網頁已經很少能不去存取其他網站,例如掛個「噗浪」「fb」…,但偏偏XMLHttpRequest不充許跨網站去存取。

後來有人發展出了JSONP(JSON with Padding),JSONP利用Javascript的callback機制,繞過Browser的安全限制,關鍵在動態載入<script src="...">,利用src來載入外部JSON資料或Javascript function到網頁中。

我們先來看動態建立Script

var Script = document.createElement('script');
Script.type = 'text/javascript';
Script.src = 'http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.4.4.min.js';
document.getElementByTagName('head').[0].appendChild(Script);

這相當於在head最下面新增一段script tag:

<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.4.4.min.js"></script>

這差不多就是JSONP的基礎了,透過動態建立script及Javascript callback,來載入跨網站JSON資料。

最後我們來看整個JSONP的運作流程示意程式碼,注意程式的流程,Step 1在Client--> Step 2在Server--> Step 3 回到Client。

Client端:*.html

// 使用jQuery

// Step 1:送出JSONP請求,
$(function(){
  $('#send').click(function(){
    // 最後必須是 &callback=funcion 或 &jsoncallback=function 結尾
    var URL = "http://abc.com/jsonp.aspx?id=1&name=Bruce&jsoncallback=dosomething";

    $.ajax({
      type : 'GET',
      dataType : 'jsonp',  // 記得是jsonp
      url : URL,
      error : function(xhr, error){ alert('Ajax request error.');}
    })
  })
});

// Step 3:收到伺服器回應後(response.write(jsonp)),執行的callback的function

function dosomething(jsonData){
  // jsonData會取得Server傳回{...json data ...}
  alert(jsonData.name);
  alert(jsonData.age);
  alert(jsonData.sex);
}

在建立非同請求時,在網址最後加上「&callback=函數名稱」或「&jsoncallback=函數名稱」,就可以取得其他網域的資料並執行後續callback的函數。

Server端 *.aspx
' Step 2:進行相關處理

'接收到的id & name處理...

' 以下是重點,組合出function及json資料,然後回傳
Dim jsonp AS String = 
' dosomething是 function name
' {...} 是json data 
jsonp = "dosomething({ _
  ' ... json data ... _
})"

Response.Write(jsonp)

Reference:

SSMS -- Backup Database Object to T-SQL file step

MS SQL Server本身有強大的備份機制,但有時候我們想把資料庫備份之像昨天「Northwind and Pubs Database T-SQL script files」那種T-SQL檔案,再利用sqlcmd來執行*.sql,也是可以及方便,各位可以參考這篇「[SQL Server 2008]使用SQL Server Management Studio內建的功能匯出資料庫SQL Script(含資料)」但我發現二件事,一是它使用的是英文版,二是他的SSMS UI與我有差異,我想可能我的是最新的MS SQL Server 2008 R2正體中文版的關係,所以我重新抓圖,算是一篇參考別人筆記的筆記。

我們來備份AdventureWorks資料庫好了,原始AdventureWorks資料庫172MB,先說,單純把AdventureWorks備份成*.sql檔案要 500MB以上,依設定不同會有所差異,例如,包含If NOT EXISTS設為true。

Backup Database Object to T-SQL file step

step 1:選擇資料庫 --> 工作 --> 產生指令碼;


Step 2:選擇備份項目;


這裡很簡單,你可以「全資料庫」或「單一、多項」選擇。

Step 3:按下「進階」設定;


這裡很直覺,我就不解釋了。

Step4:「進階」設定(項目很多,可以把畫面拉長);


以備份全資料庫而言,我覺得重點有兩個:

  • 要編寫指令碼的資料類型
  • 針對伺服器版本編寫指令碼
Step 5:要編寫指令碼的資料類型


很明顯了,預設是「僅限結樣描述」,如果是結構描述,備份AdventureWorks不到2MB,但如果是「結樣描述和資料」那就要500MB以上。

Step 6:針對伺服器版本編寫指令碼


這個重要的是「相容性」,例如,我們本機的測試用資料庫通常會比較新,而公司正式資料庫通常沒什麼人敢動,所以版本上會有差異,所以這裡要注意你選擇的版本。

Step 7:其他設定

其他設定不是說不重要,依你的需求來選擇,像「包含If NOT EXISTS」或「編寫發程序的指令碼」…等,請自行選擇及參考MSDN。

Step 8:確定後,看一下相關設定,沒問題的話就可以備份為T-SQL檔案。

另外,在Step 4的進階設定,我們可以修改SSMS的預設值,在「工具」--> 「選項」 --> 「SQL Server 物件總管」 --> 「指令碼」,即可修改預設值。以上在要把資料庫、資料表、結構描述、資料…備份到T-SQL檔案,非常好用,學起來。

Reference:

Northwind and Pubs Database T-SQL script files

留個備份,每次要重新安裝SQL Server 2000 Sample Databases( Northwind, Pubs),就要重新安裝SQL2000SampleDb.msi,還必須在C:\下留一目錄,不喜歡。

將instnwnd.sql及instpubs.sql壓縮備份在這裡,需要時立即取用。

下載Northwind_pubs_sqlscript.7z


Reference:

SQL Tips -- SQL Server 2005新增比較運算子

有兩個比較運算子我沒看過(或沒去注意),「!>」與「!<」,「!=」這個是不等於,這個我有看過,我在SQL語法裡比較少用,因為有正式「<>」不等於。

!> 不大於, !< 不小於

這兩個使用起來還算直覺,我們寫一個T-SQL來試試:



-- Northwind.dbo.Orders OrderDate between 1996-7-4 And 1998-5-6
declare @date datetime = '19970101' -- 取中間資料

select top 10 ShipName,ShipAddress
from Northwind.dbo.Orders
where OrderDate !> @date -- 不要大於1997-01-01的資料

select top 10 ShipName,ShipAddress
from Northwind.dbo.Orders
where OrderDate !< @date; -- 不要小於1997-01-01的資料


請注意,以上三個「!=」「!<」「!>」是在MS SQL Server 2005才新增的比較運算子。

Reference:

Windows 7 64bit無法啟動MS Server Server 2008 R2

在一台Windows 7 64bit 作業系統上,無法啟動MS SQL Server 2008 R2,查詢事件檢視器:


記錄檔名稱:         System
來源:            Service Control Manager
日期:            2011/1/16 上午 09:27:36
事件識別碼:         7038
工作類別:          無
等級:            錯誤
關鍵字:           傳統
使用者:           不適用
電腦:            INB01.??
描述:
由於下列錯誤,MSSQLSERVER 服務無法使用目前設定的密碼以 .\?? 身分登入: 
登入失敗: 不明的使用者名稱或錯誤密碼。

若要確保正確設定該服務,請使用 Microsoft Management Console (MMC) 中的 [服務] 嵌入式管理單元。
事件 Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Service Control Manager" Guid="{555908d1-a6d7-4695-8e1e-26931d2012f4}" EventSourceName="Service Control Manager" />
    <EventID Qualifiers="49152">7038</EventID>
    <Version>0</Version>
    <Level>2</Level>
    <Task>0</Task>
    <Opcode>0</Opcode>
    <Keywords>0x8080000000000000</Keywords>
    <TimeCreated SystemTime="2011-01-16T01:27:36.807440500Z" />
    <EventRecordID>26983</EventRecordID>
    <Correlation />
    <Execution ProcessID="468" ThreadID="4944" />
    <Channel>System</Channel>
    <Computer>INB01.??</Computer>
    <Security />
  </System>
  <EventData>
    <Data Name="param1">MSSQLSERVER</Data>
    <Data Name="param2">.\??</Data>
    <Data Name="param3">%%1326</Data>
  </EventData>
</Event>

它會導引到http://technet.microsoft.com/en-us/library/dd349365(WS.10).aspx的解決辦法,看不出有什麼解決辦法,細想內容後來想起,我有修改過此NB上登入帳號的密碼,修改回原始安裝MS SQL Server使用的密碼,OK。

但如果你真的想不起來,那只好先修改登入帳號,先讓SQL Server能Work後,再來修改設定。

Reference:

avast 5 防毒軟體造成無法上網

家裡電腦使用Avast防毒軟體已經多年,我認為最棒的是,他們從來不「騷擾」用戶,他們稱有數億的免費用戶,但我基本上沒收過他們的EDM,他們不會收集到一堆Email資訊後,就趕快拿來做廣告…連我目前的公司都做不到。

不過近來有件事一直困擾我,就是電腦會有意無意的無法上網,Browser會出現類似:


怪的是,我查設定,ping網路,都是正常的,原以為是分享器的問題,來後換了一台新的,好沒數個小時,又出現一樣的問題。我直接接到Model上網,所有情況一樣,IP、ping…都是正常,就單單只有網頁無法連線,實在奇怪。

後來懷疑會不會是被防毒軟體擋下,隨之關閉,然後…一切正常了,把防毒開啟,又無法上網。


你可以單獨關閉「網頁防護」這一項,但想想應該不只有我碰到,上網找到這一篇,原來…我的最愛也是會犯錯。

Step 1:


Step 2:


你必須把「HTTP連接埠」中的「80」刪除,然後一切就回覆正常了。
Reference:

SQL Tips--datetime與smalldatetime直接使用+加號或-減號做日期運算

一般我們在處理MS SQL Server的日期都必須透過DateAdd()等函數來運作,其實不用那麼麻煩,如果你的資料類型是datetime或smalldatetime,可以直接透過+(加號)或-(減號)來運算,例如以下語法:



declare @date smalldatetime=getdate()
select @date N'今天', @date + 31 N'加31天', @date - 31 N'減31天'
go

這樣有沒有簡潔有力些!重點是,此方法只能使用在datetime或smalldatetime兩個類型上,其他日期類型會出錯,例如,



declare @date datetime2=getdate()
select @date N'今天', @date + 31 N'加31天', @date - 31 N'減31天'
go

就會產生以下錯誤:

訊息 206,層級 16,狀態 2,行 2
運算元類型衝突: datetime2 與 int 不相容

Reference:

sqlcmd與T-SQL指令碼

Microsoft最偉大的地方就是把「UI」發揚光大,讓人們不用面對死死的指令。但就像我在學習Linux時,X-Window一點也不想用,一點也不吸引我,我深深的被那些指令吸引一樣,grep的過濾、vim的編輯、sed/awk的整理、iptables的強大、Shell Script的自動化…處處都是指令。

在MS SQL Server中,我覺得也有一個必學的指令,那就是sqlcmd,sqlcmd讓我們不必透過SSMS工具,就能直接與MS SQL Server溝通,不論本機或遠端,對sqlcmd而言,一般的T-SQL執行根本不是問題

例如使用sqlcmd查詢資料:

在cmd.exe下輸入sqlcmd <Enter>


sqlcmd
1>


會出現 1>提示代表已經成功與本機SQL Server連線,在1>下輸入:


1>select * from northwind.dbo.customers
2>go
... (執行結果)
1>


go為批次觸發指令,當sqlcmd看到go指令,會立即執行go之前的T-SQL陳述式。然後你會發現又回到1>提示,因為之前的T-SQL已經被執行了,所以就回到1>。按quit,就可以離開sqlcmd。

透過sqlcmd來執行T-SQL指令碼檔案(即含T-SQL陳述式的檔案),只要配合「排定的工作(Task Scheduler)」,就能幫我們輕鬆進行「定時、定期、排程」的資料庫作業,而不用使用到SQL Server Agent(這裡的不用是指T-SQL能做到的事)。

我舉個簡單的Select例子,這個例子將配合「指令碼變數」來一起使用。


指令碼變數


  • 在「T-SQL指令碼檔案」檔案內,宣告變數的語法為:「 $(變數名稱) 」(好像jQuery,^_^)
  • 在sqlcmd指令使用「-v」指定變數值「-v 變數名稱="變數值" 變數名稱="變數值"…」(多個變數使用空白區隔)

先使用記事本寫個T-SQL指令碼:



use $(dbname)  -- 資料庫名稱
go
select top $(num) $(c1)  -- num取幾筆, c1欄位名稱
from $(t1) -- 資料表名稱
go


存檔為sql.txt,然後開啟本機的cmd.exe,執行以下sqlcmd指令(d:為disk,請自行更改路徑):


sqlcmd -i d:\sql.txt -v dbname="Northwind" num=10 c1=* t1=orders


「-i 為input T-SQL file」,即會出現「Northwind資料庫中從Orders而來前10筆,含所有欄位的資料」,再來一個


sqlcmd -i d:\sql.txt -v dbname="Northwind" num=5 c1=* t1=products


即會出現「Northwind資料庫中從Products而來前5筆,含所有欄位的資料」,當然你可以再加上where/order by/group by等處理。

你可以透過以上方式整理一些「T-SQL指令碼樣版」,然後在要使用或要測試時拿來使用,絕對比起你每次都要重新打Select ... From ... Where ...要來的輕鬆,久了你就會喜歡上sqlcmd,你會發現原來單純的指令一點也不單純,當然sqlcmd不單單只是這樣。

我覺得sqlcmd跟SSMS是一個互補關係,sqlcmd是一個能減輕T-SQL在重覆式語法重覆定期工作上的好幫手,SSMS則是MS SQL Server開發工具味道,SSMS就好像.NET Framework必須透過Visual Studio來開發一般,那種感覺。

常常看到有網友在論壇上發問,「如何「定時、定期、排程」執行一支ASP.NET網頁程式,來進行某項工作?」如果此ASP.NET網頁程式是進行資料庫上的工作,那你需要的正是sqlcmd (或SQL Server Agent),而不是ASP.NET。

ps.. 臨時忘記sqlcmd參數,如何快速查詢sqlcmd參數?因為是cmd的程式,所以直接輸入「sqlcmd /?」就可以了,雖然沒有Linux的man強大,但也算半個man,常用的看一下就知道了。

Reference:

SSMS--執行計畫

上一回我們講了「T-SQL偵錯工具」,這回我們談談「執行計畫」,執行計畫是以圖形化方式來顯示SQL Server所執行T-SQL運作相關資訊。

執行計畫


先首我們準備一段T-SQL

select *
from Northwind.dbo.[Customer and Suppliers by City]

然後執行第一個「顯示估計執行計畫」:


按下後,SSMS會執行運算會在下方顯示「執行計畫」,


當你將Mouse移至圖形上時會顯示細節資訊,


你就能從中參考到非常詳細的資訊,這個在我們想找出資料庫執行瓶頸時非常有用處。另有一個「包括實際執行計畫」,


「包括實際執行計畫」與「顯示估計執行計畫」差別是,「顯示估計執行計畫」是未執行T-SQL前,由SSMS所做的執行計畫,所以統計的數據精確度是由資料庫內的統計資料而言。「包括實際執行計畫」是執行T-SQL後,才會顯示執行計畫,而統計數據精確度高。

一般而言,「顯示估計執行計畫」基本上是由資料庫本身的統計資訊而來,所以可信度還不錯,執行的速度也比較快,尤其是在資料量大,或關聯(Join)很多資料表時,一般性的效能查詢,使用「顯示估計執行計畫」就可以了。

顯示用戶端統計資料


「顯示用戶端統計資料」能讓你快速了解,我們的T-SQL陳述式執行後,提供給應用程式的統計資料,


這又是一個不可多統計資訊,有三大類資訊:「查詢設定檔統計資料」、「網路統計資料」、「時間統計資料」。假設我們「伺服器往返數目」太高,想要減少.NET Framework應用程式與MS SQL Server「伺服器往返數目」,可能就要使用「SqlDataAdapter類別,批次更新屬性」來減低。

透過兩個「執行計畫」及「顯示用戶端統計資料」,能讓我們更了解我們所執行的每一次T-SQL相關效能資訊,進而找出問題或改善T-SQL。

Reference:

SSMS--T-SQL偵錯工具教學

網頁寫久了都知道,我們比較不希望在頁面裡留下T-SQL,而且應該讓網頁只做網頁的工作,資料庫的事就交給資料庫,所以會慢慢走向Store Procedure、View…,頁面裡只有留下「Store Procedure Name / View Name」及「傳遞的參數」等就好。但寫Store Procedure或View…等畢竟是在SSMS工具裡執行,所以我們要學學SSMS裡的T-SQL偵錯工具。

其實SSMS的偵錯工具很簡單,如果你使用過Visual Studio開發工具,那差不多是一個樣子,設「中斷點」然後執行「偵錯」,我們先寫一段簡單的T-SQL程式:

declare @v1 int=1  -- 初始化
declare @v2 int

select @v2=MAX(ProductID) -- 應該是77
from Northwind.dbo.Products

while (@v1 <= @v2)
begin
 select *
 from Northwind.dbo.Products
 where ProductID = @v1
 
 set @v1 += 2 -- 在這裡設中斷點, 我們想取得單數行(1,3,5,7,9...)
end;


設好中斷點,我們就按下「偵錯」,


就會開始偵錯程序,此時T-SQL並不會執行,而是會等待你給它「動作」指令,它才會一行一行或一步一步的執行,


透過「偵錯工具」可以控制T-SQL的執行流程,黃色箭頭代表「下一行要執行的T-SQL陳述式」,所以一開始是停在第一行(一開始並未執行,未執行的下一行就是第一行);再看畫面下方,


「區域變數」最重要,可以看到我們變數的變化,這對我們偵錯很重要。


「呼叫堆疊」會顯示目前的執行位置、中斷點的管理,輸出是顯示各種訊息。接下來的操作大多是在「偵錯工具」及查看「區域變數」的變化。


黃色箭頭之下各代表「逐行執行」、「不進入函式」、「跳離函式」,如果你非常確定函式不會出錯,那就可以使用「不進入函式」、「跳離函式」來加速偵錯程序,我們按幾次「逐行執行」後來看結果:


我剛執行完「set @v1 += 2」這一行,所以下一行要執行的T-SQL陳述式是「While (@v1 <= @v2)」,


看看「結果」是否有跑出我們預期的結果,1、3、5、7…是我們要的,


查看「區域變數」的變化,是否符合我們預期;完成偵錯後,就按下「停止偵錯」即可。

在MS SQL Server 2008中透過SSMS工具,我們能夠很方便的進行偵錯,這是一件很棒的事。不用看著網頁上一大堆的錯誤訊息,然後再回程式碼修改T-SQL,再重新執行網頁,再看著錯誤訊息,再回程式碼修改T-SQL…無窮迴圈。

就算你是把T-SQL寫在網頁程式碼裡,也應該在SSMS先測試無誤後,再放入到網頁程式碼中,這樣才能確保T-SQL是正確無誤,以減少不必要的錯誤。

T-SQL就應該交給專家,而SSMS就是MS SQL Server的T-SQL專家。

Reference:

ASP.NET Ajax回應JSON格式

JSON格式資料在Ajax使用上比XML還多,那ASP.NET伺服器如何傳送JSON格式的資料呢?其實只是回傳「純文字」,但內容必須符合「JSON格式」,再交由前端(如jQuery)來處理即可,jQuery在傳送Ajax要求時會指定dataType="json"或$.getJSON(),所以它會知道你要傳送JSON格式的資料回來。

Ajax取得JSON資料
$(function(){}
$.getJSON("City.aspx",{show: true}, function(JSON){
  //根據傳回的JSON,建立
  var JSONOptions = '';
  for (var i = 0, len =JSON.length; i < len; i++) {
    JSONOptions += '' + JSON[i].optionDisplay + '';
  }
})

City.asp產生JSON回傳給
Response.ContentType = "text/html"
Response.CacheControl = "no-cache"
Response.AddHeader("Pragma", "no-cache")

' 依JSON格式組合出你的資料
Dim JSON AS String = ""
JSON = "[
{optionValue:1, optionDisplay: '基隆市'},{optionValue:2, optionDisplay: '台北市'},
{optionValue:3, optionDisplay: '新北市'},{optionValue:4, optionDisplay: '桃園縣'},
{optionValue:5, optionDisplay: '新竹市'},{optionValue:6, optionDisplay: '新竹縣'},
{optionValue:7, optionDisplay: '苗栗縣'},{optionValue:8, optionDisplay: '台中市'},
{optionValue:9, optionDisplay: '彰化縣'},{optionValue:10, optionDisplay: '南投縣'},
{optionValue:11, optionDisplay: '雲林縣'},{optionValue:12, optionDisplay: '嘉義市'},
{optionValue:13, optionDisplay: '嘉義縣'},{optionValue:14, optionDisplay: '台南市'},
{optionValue:15, optionDisplay: '高雄市'},{optionValue:16, optionDisplay: '屏東縣'},
{optionValue:17, optionDisplay: '台東縣'},{optionValue:18, optionDisplay: '花蓮縣'},
{optionValue:19, optionDisplay: '宜蘭縣'},{optionValue: 20, optionDisplay: '澎湖縣'},
{optionValue:21, optionDisplay: '金門縣'},{optionValue: 20, optionDisplay: '連江縣'}]"

' 回應給前端
Response.Write(JSON)

Reference:

ASP.NET -- 發送內嵌圖片Email

在前篇「當Data URI碰上Email」誤把內嵌圖片當成Data URI,實在抱歉,還有好網友提醒,不然實在不查,寫了也就忘了。

EMail的圖片處理

我們先了解EMail裡的圖片,當我們收到一封圖文並茂的Email,裡面的圖片怎麼顯示出來,或正確的說來源從那裡來?

一般而言Email圖片來源有三種:
  1. 附件
  2. 網址
  3. 內嵌
附件這常用,就不多解釋,但早期以附件最多。網址,也就是在Email裡的HTML的img標籤中src屬性設定為「網址」,當我們打開Email,它會幫我們載入這些網址所對應的圖片,好處是Email容量不會長的很胖,想想當Email圖用的越多,你又把它全部以附件附在裡面,那可是會變成小胖妹!

最後的「內嵌」,想成我們在打Word,在Word內插入一張圖片,這張圖會被Word實實在在的複製一份到Word裡,所以插入的越多,Word就越胖。但也就是因為圖片都已經含在Word檔案裡,所以你只要帶著這份Word檔案,不管到那裡,只要打得開,你都能看到Word檔案裡的圖片,而且不用把圖片和Word都必須複製一份放在一起才行。

Email的內嵌圖片原理大致上與Word插入圖片相似,這也就是我碰到的那可以直接顯示圖片Email所用的技術。

那好奇,我們ASP.NET可不可以?是的,一點問題也沒有,而且還很簡單,我們直接看Code,我在首頁放一個「Button控制項」及「Label控制項」:

Protected Sub Send_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Send.Click
  Dim m As New MailMessage()
  m.From = New MailAddress("發信者Email")
  m.To.Add(New MailAddress("收件者Email"))

  ' http://msdn.microsoft.com/zh-tw/library/system.net.mail.mailmessage.attachments.aspx
  ' Attachments, 有s,這是集合,也就是可以含很多檔案
  m.Attachments.Add(New Attachment("圖片路徑"))
  ' ContentID:http://msdn.microsoft.com/zh-tw/library/system.net.mail.attachmentbase.contentid.aspx
  ' 重點一:必須設定這個附件MIME內容ID,名稱可以自訂
  m.Attachments(0).ContentId = System.Guid.NewGuid.ToString()
  ' ContentDisposition.Inline:http://msdn.microsoft.com/zh-tw/library/system.net.mail.attachment.contentdisposition.aspx
  ' 重點二:附件的展示資訊,設定為內嵌(Inline = True)
  m.Attachments(0).ContentDisposition.Inline = True

  m.Attachments(0).NameEncoding = Encoding.UTF8
  m.SubjectEncoding = Encoding.UTF8
  m.BodyEncoding = Encoding.UTF8

  m.Subject = "內嵌KKBruce個人照測試"
  m.IsBodyHtml = True
  ' 第三個重點,img的src屬性與a的href屬性必須是"cid:"接我們剛才設定的ContentID
  ' 這樣就能直接讀取Email裡內嵌的圖片
  m.Body = String.Format("<img src=""{0}"" alt=""這是我金剛個人照"" /><br /><a href=""{0}"" target=""_blank"">點擊看照片</a>", "cid:" & m.Attachments(0).ContentId)

  Dim s As New SmtpClient
  s.Port = 25
  s.Host = "ServerIP"
  ' 以下為SMTP認為之帳號及密碼
  s.Credentials = New System.Net.NetworkCredential("帳號", "密碼")

  Try
    s.Send(m)
    state.Text = "傳送成功。"
  Catch ex As Exception
    state.Text = "傳送失敗:" & ex.Message
  End Try
End Sub

重點我都寫在註解裡了,主要是透過ContentId與ContentDisposition.Inline兩個屬性來實作出內嵌圖片的Email,讓我們來看看結果。


這是一封有「附件」的Email,


將滑鼠移至「點擊看照片」,你就能看到提示「cid:GUID」,cid:名稱,只要與ContentId相符即可。

我們來看原始檔內容:

<img src="cid:3f041322-0525-4fb7-b28a-377ba9313239" alt="這是我金剛個人照" /><a href="cid:3f041322-0525-4fb7-b28a-377ba9313239" target="_blank">點擊看照片</a>

Where in KKBruce Photo?

這種內嵌圖片很好玩,雖然Outlook提示是有附件,但你找不到,就算你使用「儲存所附件」,


依然空空。

其實它已經和Email合為一體了,在Outlook 2010,你必須選擇「檔案」「資訊」「內容」 :

…以上刪除…
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: base64

Content-Type: application/octet-stream; name=kingkong.jpg
Content-Transfer-Encoding: base64
Content-Disposition: inline
Content-ID: <3f041322-0525-4fb7-b28a-377ba9313239>
…以下刪除…

找一下有沒有看到很相似的東西,這就是我們的那張圖,名稱是kingkong.jpg,其他就如同我們的設定。

最後,我還沒有找到讓Outlook 2010不要直接顯示內嵌圖片的設定,如果你知道,麻煩跟我說一聲。

ASP.NET Ajax應加Header

' 給予正確的ContentType
Response.ContentType = "text/xml"
' IIS不要快取
Response.CacheControl = "no-cache"
' Browser不要快取
Response.AddHeader("Pragma", "no-cache")

Reference:

新通用頂級網域名稱(New gTLD)新知加嚇人的費用

如果你上網時看到的結尾是二字元結尾,如.tw、.hk、.jp、.cn等稱為國家頂級網域名稱(Country Code Top Level Domain, ccTLD),或者三字元結尾,如.com、.org、.net等稱為通用頂級網域名稱(Generic Top Level Domain, gTLD)

目前全球的gTLD還不是很多,但網際網路名稱與號碼分配機構ICANN(Internet Corporation for Assigned Names And Numbers)有意開放新的gTLD讓人申請,也就是我也可以申請一個".bruce"或以國家全稱為單位的域名,如".taiwan",或是公司品牌,如".google"、".facebook",這樣的域名稱為新通用頂級網域名稱(new gTLD),這樣的域名更簡潔更直觀。

但這不是給平民百姓玩的,在ICANN的文件中明白寫下申請費用為US 185,000,以1:30換算約5,550,000新台幣。

然後是有一家台灣的廠商,最近一直打廣告說,可以幫忙申請new gTLD,我很好奇去問了一下,他們的規劃書有個字眼是「遊說」,遊說要錢的,零零總總費用是US 300,000以上,以1:30換算約9,000,000新台幣。

人客呀,如果你未來(預計2012上路)在網上看到這種超貴、超頂級的域名,拜託請起立,跟它鞠躬一下,因為你看到的是一個價值近「億」的域名,這大概就是Domain裡的超跑吧!

C# to VB之隱含函式

C#之隱含函式

x => x.姓名

C#來源:ASP.NET MVC 2 開發實戰

VB之隱含函式

function(x) x.姓名

所以看到C#裡的「=>」就是VB的function() ...的意思,這個就是語法上差異很大的例子。

Reference:

當Data URI碰上Email

新年將至,Email裡的Anti-Spam也多了起來,賣藥、股票、發票、貸款、在家創業…等一堆,但有一封Anti-Spam引起我的興趣。

第一,它能正確到我的收件匣;
第二,它能在不經過我同意下顯示出圖片

我使用的是Outlook,信件都是先經過TrendMicro Solution,再進到Outlook,Outlook本身的辨識率也還不錯,應該有95%以上,都能正確把Anti-Spam送進「垃圾郵件」中,就算有漏網之魚,一般而言,圖片會先經過使用者同意後才會顯示,例如,


但這封Anti-Spam,可以無視Outlook的安全設定,直接顯示圖片,如:


因為不是第一次了,感覺到不對,查看了一下原始碼,原來:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>

    
  </head>
  <body bgcolor="#ffffff" text="#000000">
<center>
<img border="0" src="cid:0271d15f7b22$b60be514$563c53f5$AAIVYSH" width="550" height="350" align="center">
</center><br>
Salvador Bailey
  </body>

</html>


看出來了嗎?它img的src路徑怪怪的,那是什麼東東?那個東西稱「Data URI」,所以結論是,使用Data URI可以的Email可以「繞過」Outlook的安全設定。

技術真是兩面刃,Data URI是個好東西。槍可以殺人,也可以救人,依使用者而定。

下載使用Data URI的Anti-Spam (7z)


以上郵件是使用「內嵌圖片」技術,細節我在另寫一篇「ASP.NET -- 發送內嵌圖片Email」解釋。感謝網友的提醒。

Reference:

C# to VB之ADO.NET實體資料模型之新增資料

C#之ADO.NET實體資料模型新增資料語法

MvcApplication1.Models.MvcGuestbookEntities db = new Models.MvcGuestbookEntities();

db.AddTo留言板(new Models.留言板()
{
  姓名 = data.Name,
  Email = data.Email,
  內容 = data.Body,
  建立時間 = DateTime.Now
});

C#來源:ASP.NET MVC 2 開發實戰

VB之ADO.NET實體資料模型新增資料語法

' 不用使用完整的命名空間MvcApplication1.Models.MvcGuestbookEntitie
' VB會自動判別,直接使用物件即可
Dim db As MvcGuestbookEntities = New MvcGuestbookEntities()
'Dim db As New MvcGuestbookEntities() 是VB更簡潔語法,建立加初始化

' 在Visual Studio 2010中,最後面的_(底線)可以不用加
db.AddTo留言板(New 留言板() With { _
  .姓名 = data.Name, _
  .Email = data.Email, _
  .內容 = data.Body, _
  .建立時間 = DateTime.Now _
})

db.SaveChanges()

原先是被C#裡的這一段卡住:

db.AddTo留言板(new Models.留言板()
{
  姓名 = data.Name,
  Email = data.Email,
  內容 = data.Body,
  建立時間 = DateTime.Now
});

來後使用語法轉換服務,轉出來的VB是:

db.AddTo留言板(New Models.留言板() With { _
 Key .姓名 = data.Name, _
 Key .Email = data.Email, _
 Key .內容 = data.Body, _
 Key .建立時間 = DateTime.Now _
})

我就看懂了。

Reference:

C# to VB之函式屬性

就很多資料都是C#,所以必須學著把C#轉換為Visual Basic。基礎轉換還沒有什麼問題,MSDN最好了,什麼都給你多重語法,只要有Code,要什麼語法就有什麼語法。

反正都已經花時間轉下去了,所以只要看到不會的C#語法,又被我轉換成功的,就會發上來當筆記。

C#屬性
[HttpPost]
public ActionResult Save()
{
//Do something
}

VB屬性
<HttpPost>
Public Function Save() As ActionResult
  ' Do something
return View()
End Function

C#是[...]而VB是<...>。

Reference:

ASP.NET MVC學習資源

書及網站

  1. ASP.NET MVC 2開發實戰
    目前唯一繁體中文講解ASP.NET MVC的書,寫的很不錯,因為作者是實戰派實力派,所以可以說是不留私的一本實用書。此書只有C#範例,如果你使用VB,那就必須苦一點,我是還沒有看到ASP.NET MVC for VB的書籍,所以學著看C#的Code吧。我沒學過C#,但看久了也還好,除非是一些新技術或語法差異性很大的內容(如隱含函式),不然以VB來看C#,60% ~ 80%以上看懂C#應該還沒有什麼問題。另外作者保哥的部落格也是一定要去看的。
  2. ASP.NET MVC Tutorials
    這是微軟官方教材,內容為全英文,K吧!通常都是短篇、一個小主題的講解,最棒的事「它已經幫各位寫好for VB或for C#」兩個版本,依你所需去學習即可。
  3. 點部落
    這裡分享人與文章很多,可依需求及關鍵字做查詢,可當成學習特定主題時,找資料的來源。
  4. ASP.NET專題實務II:範例應用與4.0新功能(附光碟)
    雖然不是講ASP.NET MVC的專書(只有第十八章  ASP.NET MVC Framework),但MIS2000Lab的部落格上資料可是很多,另外他的「專題實務」系列都有VB與C#版本可選擇,不用怕找不到合適的內容,有任何書上內容的問題,他都很熱情回答。

C# to VB -- 程式語法轉換服務

如果跟我一樣,常找到的資料只有C#的Code,而且又看不懂時,可以借助以上兩個網站所提供的程式語法轉換服務,雖然不是100%正確,但常輪廓出來後,我大約就沒什麼問題了。而且這些程式語法轉換服務不只是單向提供語法轉換,而且你想轉VB to C#、C# to VB、VB/C# to Python、VB/C# to Ruby…可以雙向轉換,真的是太Cool。

速查表(Cheat Sheet)


這個網些整理了很多速查表,也是放在手邊查詢不可多得的資料。

Reference: