ASP.NET MVC 3.1 使用Scaffold產生Controller, Action與View

在ASP.NET MVC中,如果你是Model First或Database first,那可以使用Scaffold ( 它就像使用樣版來產生程式架構)來加速你MVC程式的開發。在 ASP.NET MVC 3.1 (等一下我們解釋) 中透過與 Entity Framework 的合作,再加上Scaffold,我們不只可以產生Controller, Action,連View 都可以一併產生,而且是一個可完整使用CRUD程式。如果你的Model或Batabase設計的很好,使用ASP.NET MVC 3.1的Scaffold功能,是有可能連一行程式都不用寫就可以產生一個網站。

參考:Julie的「MVC3.1 Scaffolding Magic with Database (or Model) First , Not Just Code First」(MVC 3.1是從這裡來的)

我的步驟和他一點點不同:(以Visual Basic and Northwind為例)
  1. 產生ASP.NET MVC 3專案
  2. 加入新專案,類別庫
  3. 在類別庫中,新增「ADO.NET 實體資料模型」,依序產生 *.edmx
  4. 由 *.edmx 產生 DbContext ( *.tt )
  5. 回到ASP.NET MVC 3專案,將 app.config 的連線字串複製到 web.config
  6. 參考「類別庫」專案
  7. 到 Controllers 目錄,新增控制器
  8. 修改Global.asax,將 .controller = "Home" 修改為你想要的 Controller,例如,.controller = "Employees"
  9. Ctrl + F5

如果你要使用 ASP 或 ASP.NET 花CRUD就要花費你多少時間,使用使用ASP.NET MVC 的 scaffold 功能,可以快速幫我產生所有 CRUD 的 controller, action, view,讓我們花更少的力氣可以做更多的事。

ASP.NET MVC 基本開發流程

1. Database 規劃( 3NF / BCNF ) --> 2. Model 產生 ( *.edmx / *.tt ) --> 3. 設計Controller / Action --> 4. View

1. Database 規劃( 3NF / BCNF )

我走的是Database First,所以把資料庫放在第一順位。如果你是Model First或Code First,請自行調整。
快速正規化入門:
  1. 1NF:單值。
  2. 2NF:PFD;R(X, Y, Z, A),X --> Y, Z,A不能被X決定,A為PFD。
  3. 3NF:非PK,不存在FD;R(X, Y, Z, A, B),X -->Y,Z,A,B;A --> B;A為非Key,但能決定B。
  4. BCNF:R中所有「決定屬性(唯一性)」均為CK。R(X, Y, Z, A, B), X,Y --> Z,A,B;A --> B;A非CK,但能決定B。

2. Model 產生 ( *.edmx / *.tt )

這部份很簡單與複雜。
簡單的是,百分之百都是由工具產生。複雜的是,*.edmx、*.tt這些檔案內容。

3. 設計Controller / Action

Controller只是命名,比較有關係是Routing。大部份時間都是在寫Action。
Action做三件是:
  1. DataBinding 設定
  2. 驗證資料
    1. Bind 屬性設定
    2. DataAnnotations 的Class設定
  3. 撰寫程式碼

4. View

當資料由Action傳送過來,我們要如何「顯示」。

Download all MSDN and TechNet Library Document

我們走MS這條路的人,一定無法避免看MSDN,這是MS的聖經。有時候反方面想想,很少有公司願意花那麼多時間、金錢在搞這種東西。一個完整的參考資料來源對我們而言是非常重要的。

在 透過Help Library Manager時,我一直有個疑問,為什麼不讓我們可以選擇下載完整版的MSDN?在很早期之前,可以和MS可以簽MSDN合約,記得是每一季會寄一份最 新版的MSDN光碟(最早是CD後來改 DVD),讓我們可以透過光碟來安裝所需的Library,可以因為種種原因,就不在寄送光碟了。雖然網頁很方便,但還是不及離線安裝在自己本機來的好, 如不能上網,一切免談!

最後,我實在忍不住,我上去 connect 去問MS,等了近一個月後,終於給我一個滿意的答案。

Help Team回答:
Greetings,

Thank you for your suggestion. There is a CodePlex project, PackageThis (link below), which enables selection of online MSDN content and generates a package of selected content for addition to the local Help store.

http://packagethis.codeplex.com/SourceControl/list/patches

The latest patch contains an update for the SP1 Help Viewer format (MSHC).

The Help Team

他們提到一個軟體Package This,這個軟體可以將MSDN及TechNet上所有的文件下載成為離線檔案(mshc格式),然後匯入Help Library Manager。

先感謝 H3Viewer 的作者,我之前就介紹過了( H3Viewer,我最推薦的MSDN閱讀及管理工具 ),想不到最後解決我問題的還是他,他提供了另一個超棒的工具 Package This, 這個工具可以讓我們下載所有的MSDN及TechNet文件,然後匯出成 *.chm 或 *.HxS (Visual Studio 2008 ~ 2002之前 Help使用格式) 或 *.Mshc ( Visual Studio 2010 Help 使用格式 )。

我們先下載 Package This! 1.3.4.0版,直接執行,


Package This

下載執行Package This,首先選擇「Library」,看你是要下載「MSDN」或「TechNet」的文件,然備選擇「Locale」,擇選你要的語系。例如,我要下載MSDN裡的ASP.NET MVC 3文件,因為ASP.NET MVC 3文件還沒有正體中文,所以我們的Locale必須選擇「英文(美國)」。如果是要下載ASP.NET MVC 2,那Locale就可以選擇「中文(繁體,台灣)」。

圖一:

勾選「Select This Node and All Children...」,可以選擇目前節點之下所有內容。(它會一個一個勾選,時間會有點久)

旁邊的空間區域也會開始跑出相關內容,

圖二:

等全部勾選完畢,就可以執行File → Export Mshc File,將勾選的文件匯出成*.mshc。我下載ASP.NET MVC 2正體中文全部內容約 31MB左右。

圖三:

 Product Name及Book Name修改成你想要的名稱。按下OK,即可匯出mvc3r.mshc。(匯出也需要一些時間)

匯出*.mshc完成之後,從Package This的File → Install Mshc Help File,它會提醒需要管理者權限,按下Yes,即可匯入。

圖四:從Package This匯入

這裡會選擇一個安裝設定檔,它會幫我們設定相關安裝參數。如果不想切換說明文件的目錄,可以將最後一個參數改為「zh-TW」,離線文件會安裝到相同的目錄下。

圖五:從Disk安裝

圖六:需要管理者權限

圖七:合併索引


圖八:從刪除內容確認安裝


開啟H3View,即可看到我們想要的離線說明文件。

圖九:H3View
 
有了Package This,你就可以擁有一個完整的MSDN與TechNet的技術文件,讓你想怎麼看就怎麼看。

小禮物:

ASP.NET MVC 3 Reference for Mshc File
ASP.NET MVC 2 參考 for Mshc File

解壓縮密碼:kkbruce

LINQ and Entity Framework筆記

Entity Framework 架構


圖一

圖二

LINQ 分頁


Function GetPaging(index As Integer, size As Integer) As IEnumerable
Dim db As New NorthwindEntities()
' 要Skip的筆數
Dim SkipPageSize As Integer = index * size
Dim result = (From n In db.Products …).Skip(SkipPageSize).Take(size)
Return result

End Function

LINQ 是否有資料


If (result.Count() = 0) Then
' 無資料處理

End If

LINQ 預先編譯


' 1.宣告全域變數
ReadOnly cq = System.Data.Objects.CompiledQuery.Compile(Of QnoDCEntities, DateTime, IQueryable(Of Orders))(Function(ctx, orderDate) From o In ctx.Orders Where o.OrderDate < orderDate Order By orderDate Select o)

' Compile(Of 實體, 參數1型別, 參數2型別, … , 參數n, IQueryable(Of 物件)) 參數型別依查詢式所需,可以有多個。  IQueryable(Of 物件)即Data Source。


Function(實體參數, 參數1名稱, 參數2名稱, … , 參數n名稱)  From o In DataSource … 此處的DataSource必須與IQueryable(Of 物件)對應  Where … > 參數1名稱 AndAlso ... < 參數2名稱 … 以參數進行過濾

' 2.主程式
Dim Order = cq.Invoke(QnoDCEntities, orderdate) 傳入實體及參數

' 可參考:http://msdn.microsoft.com/zh-tw/library/bb896297.aspx

LINQ 之CURD

' LINQ to Entity
' 新增
context.AddTo(EntityClass)(Data)
context.SaveChanges()
' 更新
result.欄位 = …
context.SaveChanges()
' 刪除
context.DeleteObject(Data)
context.SaveChanges()

' Entity Framework 4.1
' 新增
context.物件.Add(Data)
' 例如: context.Blogs.Add(blog)
context.SaveChanges()
' 更新
Dim blog = context.Blogs.Find(id)
context.Entry(blog).State = System.Data.EntityState.Modified
db.SaveChanges()
' 刪除
Dim blog = context.Blogs.Find(id)
db.Entry(blog).State = System.Data.EntityState.Deleted
db.SaveChanges()

串連 LINQ


Function Query1(…) As IQueryable(Of …)
    Return From … In …
End Function

Function Query2(Q1 As IQueryable(Of …), …) As IQueryable(Of …)
    Return From … In Q1 …
End Function

把Query1當成Query2的Data Source。

LINQ交易

1. SaveChanges() 已預設使用交易。
2. 兩個DataContext:使用Explicit交易。
3. 兩個Database:使用TransactionScope交易。

EDM自動跳號

1. Identiry (自動跳號)
2. computed ( Insert, Update都會更新),例如,DateTime型別。

關聯載入

1. 預設Lazy Loading (需要時才載入)
2. Load() 方法
3. Include(…) 方法

Create Query

方便組裝E-SQL。(Dynamic Query更好)
例如,
Dim Rusult = context.CreateQurey(Of …)(“E-SQL”, ObjectParameter(“Name”, …))

Entity Client

用法與SqlClient一樣。
EntityClient + E-SQL。
SqlClient + T-SQL。

E-SQL

1. 使用別名。
2. Select Value 別名。

Dynamic Query

Dim Result = context.Orders.Where(“it.OrderId = @Id”, New ObjectParameter(“Id”, …))

it是關鍵字。

EDMX 之 POCO

.edmx --> 非POCO,繼承EntityObject --> 序列化(XML)肥大  Web Service
EF 4.1 --> 轉換為POCO --> 序列化(JSON) --> WCF

DbContext

ASP.NET MVC可使用DbContext。(EF 4.1)

' 新增:context.Orders.Add(Data)
' 更新:
' 刪除:context.Orders.Remove(Data)
' 更OOP。

State

改變State可更新、刪除(配合Find())
' 找到Data
Dim Result = context.Orders.Find(“PK”)
context.Entity(…).State=EntityState.{Modified | Deleted}
SaveChanges()

批次新增

批次新增可先Disable Changes Tracker
db.Configuration.AutoDetectChangesEnabled = False
批次新增完成後,再Enable。

單純Query

單純Query,使用No Tracker
Dim Result = From o In db.Orders.AsNoTracking() Select o

Local查詢

Local技術,將連線資料處理模式帶到離線資料處理模式。
Dim Result = From o In db.Orders.Local Where o.SerialNumber.StartsWith("AK42") Select o

使用T-SQL

' 查詢
Dim Result = db.Orders.SqlQuery("select * from orders")
' 異動
Dim Result = db.Database.ExecuteSqlCommand("Insert ... Value {0}, {1}", 參數1, 參數2)

DbContext使用E-SQL

Dim Result = CType(db, System.Data.Entity.Infrastructure.IObjectContextAdapter)
.ObjectContext.CreateQuery("E-SQL")

ASP.NET 2.0 表單身份認證心得筆記

在網網相連中,看到一篇「Explained: Forms Authentication in ASP.NET 2.0」覺得寫的很好,雖然第一行的黃色字有點…,還是自己寫點筆記留個記錄。

ASP.NET身分驗證流程有二:IIS --> ASP.NET。

第一,一般對外網站在IIS部分都會使用匿名身份驗證(anonymous authentication),然後將使用傳遞給ASP.NET。
第二,ASP.NET執行自己的身份驗證。我們可以在 web.config 設定檔裡由authentication設定 mode 屬性來決定驗證的模式。

FileAuthorizationModule 類別
<authentication mode="Forms" />

ASP.NET 表單身分驗證

在ASP.NET裡,我們一般都是使用表單來驗證使用者。我們可以在 web.config 設定檔裡設定表單身分驗證預設屬性值。

<system.web>
  <authentication mode="Forms">
    <forms loginUrl="Login.aspx"
           protection="All"
           timeout="30"
           name=".ASPXAUTH" 
           path="/"
           requireSSL="false"
           slidingExpiration="true"
           defaultUrl="default.aspx"
           cookieless="UseDeviceProfile"
           enableCrossAppRedirects="false" />
  </authentication>
</system.web>

forms 預設屬性值

  • loginUrl
    指定指向您的應用程式自訂的登入頁(Login.aspx)。
  • protection
    保護所有設置,身份驗證ticket,會使用machineKey元素來進行加密。
  • timeout
    用於指定的表單身份驗證Session的有效時間。預設值為 30 分鐘。
  • name
    name和path設置為應用程式的設定檔中定義的值。
  • requireSSL
    此設定是指該 cookie 可以傳輸不是 SSL 加密的身份驗證。如果您擔心Session劫持,則應考慮為true。
  • slidingExpiration
    執行滑動的Session生存期設定為true。只要在網站上的使用者仍處於活動狀態,定期重新設定timeout的Session。
  • defaultUrl
    Default.aspx 預設首頁。
  • cookieless
    無cookie來指定應用程式使用的所有流覽器都支援 cookie 的 cookie 設置為UseDeviceProfile。如果流覽器不支援 cookie 訪問該網站,然後 forms 身份驗證套裝程式 URL 上的身份驗證票。
  • enableCrossAppRedirects
    指示該表單身份驗證不支援自動處理的查詢字串或表單 POST 的一部分的應用程式之間傳遞的ticket。

通過身份驗證認證後,我們還必須給適當的權限。

授權配置

UrlAuthorizationModule 類別 驗證使用者確實擁有權限,可以存取所要求的 URL。

透過 authorization 元素來設定UrlAuthorizationModule

<system.web>
  <authorization>
    <deny users="?" />
  </authorization>
</system.web>

此使此設定後,未經身份驗證的所有使用者都被拒絕訪問您網站的任何頁面。而且會將使用者重新導向登入頁(loginUrl屬性)。

表單身份驗證流程


  1. 使用者請求 Default.aspx 從您的IIS應用程式。IIS 允許請求,因為 IIS 啟用匿名訪問。ASP.NET確認授權的元素包括<deny users="?" />。
  2. 伺服器將尋找為身份驗證 cookie。如果未能找到身份驗證 cookie,則將使用者重導向到設定的登入頁 (Login.aspx)。起始頁的資訊放在RETURNURL作為Key的查詢字串。伺服器的 HTTP 答覆如下所示:
    302 Found Location:
    http://localhost/FormsAuthTest/login.aspx?RETURNURL=%2fFormAuthTest%2fDefault.aspx
  3. 瀏覽器請求 Login.aspx 頁面,並在查詢字串中包含RETURNURL參數。
  4. 伺服器將返回登入頁面和 200 OK HTTP 狀態碼。
  5. 使用者在登入頁面上輸入帳號密碼,返回到伺服器。
  6. 伺服器驗證使用者帳號密碼,從如 SQL Server 資料庫或AD。在登入頁面中的撰寫程式碼,建立一個包含表單的身份驗證ticket為Session設定的 cookie。
    我們可以使用成員資格(Membership)系統:

    If (Membership.ValidateUser(userName.Text, password.Text)) Then
    
        If (Request.QueryString["ReturnUrl"] IsNot Nothing) Then
            FormsAuthentication.RedirectFromLoginPage(userName.Text, false);
        Else
            ' 設定Cookie
            FormsAuthentication.SetAuthCookie(userName.Text, false);
        End If
    
    Else
        Response.Write("Invalid UserID and Password")
    End If
    
  7. 身份驗證的使用者中,伺服器將瀏覽器重新導向到原始 URL 查詢字串中指定的RETURNURL參數。伺服器的 HTTP 答覆如下所示:
    302 Found Location:
    http://localhost/TestSample/default.aspx
  8. 重新導向瀏覽器再次請求 Default.aspx 頁。此請求包含該表單的身份驗證 cookie。
  9. FormsAuthenticationModule類別檢測表單身份驗證 cookie,並對使用者進行身份驗證。身份驗證成功之後,FormsAuthenticationModule類別寫入HttpContext物件,有關身份驗證的使用者的資訊將被公開為屬性。
  10. 由於伺服器已驗證身份驗證 cookie,它授予存取權限,並返回 Default.aspx 頁。

FormsAuthenticationModule

在ASP.NET 2.0的本機 Web.config 設定檔(指的是 C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG 之下的 Web.config),定義了一組httpModules。裡面包含了身分驗證模組的數量。
<httpModules>
            <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
            <add name="Session" type="System.Web.SessionState.SessionStateModule"/>
            <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule"/>
            <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule"/>
            <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule"/>
            <add name="RoleManager" type="System.Web.Security.RoleManagerModule"/>
            <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule"/>
            <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule"/>
            <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule"/>
            <add name="Profile" type="System.Web.Profile.ProfileModule"/>
            <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
            <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
        </httpModules>
只有一個身分驗證模組會用於每個請求。通常我們會在網站中的 Webconfig 裡指定身份驗證模式。
<authentication mode="Forms" />
FormsAuthenticationModule類別建構一個GenericPrincipal物件,並將其儲存在 HTTP content。GenericPrincipal物件包含表示目前已通過身份驗證的使用者的FormsIdentity實例的參考。您應該允許表單身份驗證為你管理這些任務。如果你的應用程式有特別的要求,如使用者屬性設定在自訂的類別中實現IPrincipal介面,你的應用程式應該處理PostAuthenticate事件。FormsAuthenticationModule已驗證表單身份驗證的 cookie 並建立的GenericPrincipal和FormsIdentity物件之後,將觸發PostAuthenticate事件。在此程式碼中,可以建構包含的FormsIdentity物件及自訂的IPrincipal物件,然後將它儲存在HttpContext。 請注意:如果你這樣做,您還需要設定IPrincipal參考Thread.CurrentPrincipal屬性,以確保HttpContext物件和執行緒指向相同的身份驗證資訊。

表單身份驗證 Cookie

FormsAuthentication.SetAuthCookie或FormsAuthentication.RedirectFromLoginPage的方法被呼叫時,FormsAuthentication類別將自動建立身份驗證 cookie。

在典型的表單身份驗證 cookie 中包含以下屬性:

  • Name:此屬性指定 cookie 的名稱。
  • Value:此屬性指定 cookie 的值。

在典型的表單的身份驗證 cookie中,Value包含加密和簽名的FormsAuthenticationTicket物件字串表示形式。Cookie 包含以下屬性:

  1. Expires:此屬性指定 cookie 的過期日期和時間。
  2. Domain:此屬性指定 cookie 相關聯的網域。預設值為空( null or Nothing ).
  3. HasKeys:此屬性指示是否該 cookie 有次項目。
  4. HttpOnly:此屬性指定用戶端指令碼(client script)是否可以訪問 cookie。在 ASP.NET 2.0,此值始終設定為true。請注意不支援HttpOnly 屬性的 web 流覽器忽略該 cookie,或忽略該屬性,這意味著該Session仍有跨網站腳本攻擊可能。
  5. Path:此屬性指定 cookie 的虛擬路徑。預設值是"/",指定根目錄。
  6. Secure:此屬性指定是否僅應通過 HTTPS 連接傳輸 cookie。安全屬性應設置為true,這樣,cookie 受 SSL 加密。
  7. Version:此屬性指定 cookie 的版本號。

建立表單的身份驗證 Cookie

FormsAuthentication類別,如下所示建立表單的身份驗證 cookie。一旦使用者進行驗證,FormsAuthentication類別內部建立一個FormsAuthenticationTicket物件指定 cookie 的各項屬性。

' FAT(版本,名稱,核發日期,到期日期,永續性,使用者特定資料,目錄路徑)
' 使用者特定資料:userData 參數不能為 Nothing
Dim ticket As New FormsAuthenticationTicket(1,
                                            "userName",
                                            DateTime.Now,
                                            DateTime.Now.AddMinutes(30),
                                            False,
                                            String.Empty,
                                            FormsAuthentication.FormsCookiePath)

如果表單元素的protection屬性設定為All或Encryption,表單身份驗證使用的加密方法加密和簽名表單的身份驗證ticket。

Dim encryptedTicket As String = FormsAuthentication.Encrypt(ticket)

下面的內容顯示時protection屬性設定為All的過程:

  • 建立序列化的表單身份驗證ticket。建立的ticket以byte陣列表示。
  • 簽錄表單身份驗證ticket。
  • 加密表單身份驗證ticket。
  • 根據需要建立 HTTP cookie 或查詢字串。
    Dim authcooket As New HttpCookie(FormsAuthentication.FormsCookieName,
                                     encryptedTicket)
    
  • 設定表單的身份驗證 cookie 為Secure。
    authcooket.Secure = True
  • 設定HttpOnly。
  • 設定適當的 cookie 屬性。
  • 加入該 cookie 到 cookie集合。
    Response.Cookies.Add(authcooket)

每次接收到後續身份驗證請求後,FormsAuthenticationModule類別檢查身份驗證 cookie 的身份驗證ticket, 對它進行解密, 計算雜湊值中,和將有助於確保 cookie 不被篡改的 MAC (message authentication code) 值進行比較。最後,驗證的表單身份驗證ticket內所記載的過期時間。

請注意,因為此日期可以很容易偽造,ASP.NET不依賴 cookie 的過期日期。

角色授權

.NET Framework 2.0包含角色管理 API 使您能夠建立和刪除角色,並增加使用者和從角色中刪除使用者。

  • SQL Server:這是預設的Provider,並將角色資訊存儲在 SQL Server 資料庫中。
  • 授權管理器 (AzMan)。此Provider使用 AzMan 策略存儲在 XML 檔中,在AD中,或在AD中應用模式 (ADAM, Active Directory Application Mode) 作為其角色儲存。它通常用於 intranet 或外部網路 Windows 身份驗證和 Active Directory 用於進行身份驗證。

Cookieless 的表單身份驗證

表單元素的屬性控制此 Cookieless 功能。

  • UseCookies:此值強制的FormsAuthenticationModule類別使用 cookie 傳輸的身份驗證ticket。
  • UseUri:此值指示FormsAuthenticationModule類別重寫該 URL 傳遞身份驗ticket。
  • UseDeviceProfile:此值指示依看瀏覽器功能來決定,如果瀏覽器支援 cookie,就使用 cookie ;否則,該 URL 會被重寫。
  • AutoDetect:此值指示FormsAuthenticationModule類別檢測瀏覽器是否支援通過動態檢測機制的 cookie。如果檢測邏輯表示不支援 cookie,然後該 URL 就會被重寫。

如果您的應用程式設定為使用 cookieless 的表單身份驗證,並且正在使用FormsAuthentication.RedirectFromLoginPage方法,FormsAuthenticationModule類別會自動設定表單身份驗證ticket在 URL 中,例如,

http://localhost/CookielessFormsAuthTest/(F(-k9DcsrIY4CAW81Rbju8KRnJ5o_gOQe0I1E_jNJLYm74izyOJK8GWdfoebgePJTEws0Pci7fHgTOUFTJe9jvgA2))/Test.aspx

在 url 括弧中部分包含的 cookie 會包含的資料。在請求處理過程中,這些資料會被刪除。

成員資格和登入控制項

會員服務provider的憑證儲存和管理應用程式的使用者。它還提供了 API,當使用表單身份驗證時,簡化了驗證使用者憑證的工作。成員資格功能是提供provider model的基礎上建立的。此 model 允許實施和配置不同的Provider,指向不同的使用者儲存區。

  • Active Directory membership provider
  • SQL Server membership provider

您還可以增加自訂的使用者儲存的支援。建立MembershipProvider抽象基礎類別繼承的自訂provider。

ASP.NET 登入控制項自動使用成員資格和表單身份驗證,並將提示使用者輸入憑證、 驗證使用者、 恢復或更換密碼,等所需的邏輯封裝。

Web Farm

在 Web Farm中,不能保證哪台伺服器連續處理請求。如果一台伺服器上通過身份驗證的使用者和下一個請求轉到另一個伺服器,身份驗證ticket將失敗的驗證,並要求使用者重新進行身份驗證。

MachineKey元素中的validationKey和decryptionKey屬性用於雜湊演算法和加密的表單身份驗證ticket。這些屬性的預設值是AutoGenerate.IsolateApps。每個應用程式會動產生Key,而且每台伺服器上不同。為解決這一問題,validationKey和decryptionKey的值必須在 Web Farm中的所有電腦上的相同。

.NET Framework 入門筆記 -- 實值型別Value Type

如果你有一點程式基礎或學過資料結構,或許聽過 Call By Value 和 Call By Address,其實這與 .NET Framework 裡的「實值型別(Value Type)、參考型別 (Reference Type)」差不多意思。

實值型別的變數可以直接包含它自己的資料,而不是只記錄該資料位於記憶體的參考位址。Value Type的Instance是存在一個稱為「堆疊(Stack)」的記憶體區域。

一般Value Type有三種:
  • 內建型別
  • 使用者定義型別
  • 列舉
內建型別

內建的數值型別都屬於Value Type。(包含布林及日期)

常用Value Type:
  • Integer (正負21億)
  • UInteger (正42億)
  • Double
依MSDN的建議,整數以 Integer 及 UInteger 執行效率最佳,浮點數以 Double 執行效率最佳。

其他Value Type:
  • Char
  • Boolean
  • Date
比較特別的是日期,日期也是Value Type。

我們在指派Value Type變數之間的值時,資料會從其中一個變數複製到另外一個變數,儲存在堆疊中的兩個不同位置。

Dim a As Integer = 5
Dim b As Integer
' b = 5
b = a

b儲存的是a的值,也就是5,而非a的記憶體位置。

使用者定義型別

又名「結構(Structures)」,結構在大部分情況下,幾乎和類別一樣。結構是數個型別的整合,以便讓Program更容易處理相關資料。可參考我這篇「結構(Structure)關鍵字」。以效率而言,結構比類別有效率。

列舉

列舉是指擁有固定值的相關符號。你可以把列舉想像我們網頁上的「下拉式選擇」,例如,我們在選擇地址時,都會先選擇縣市,再來選鄉鎮…你能發現,這些選擇性的值都非常的固定,變動的機會非常的小,這就非常合適使用列舉。

' 注意,是回傳Integer
' 由0開始, Mr = 0, Ms = 1 ...
Enum Titles As Integer
    Mr
    Ms
    Mrs
    Dr
End Enum

使用也很簡單,就把舉列當成一般型別使用。

Dim t As Titles = Titles.Dr

' 3
Console.WriteLine(t)
' Dr.
Console.WriteLine("{0}.", t)

列舉的目的是為了簡化程式的撰寫及增進程式的閱讀性,讓你使用有意義的符號取代簡單的數值。

ASP.NET MVC 3 新HTML Helper筆記(8) -- Json Class

我們在「JSON之教學筆記」提過,原生的 .NET Framework ( VB / C# )並無支援JSON,如果你必須在伺服器端解析JSON的資料,必須下載第三方元件。現在,透過Json類別算是正式支援JSON的解析。

Json類例使用範例


HomeController.vb
Function Index() As ActionResult
    Dim db As New NorthwindEntities
    Dim customer As List(Of Customers) = db.Customers.ToList()
    ' Json會與ActionResult的Josn衝突,所以使用完整命名空間
    Dim JsonString = System.Web.Helpers.Json.Encode(customer)

    Return Json(JsonString, JsonRequestBehavior.AllowGet)
End Function

執行後,你就能看到


"[

{
\"CustomerID\":\"ALFKI\",
\"CompanyName\":\"Alfreds Futterkiste\",
\"ContactName\":\"Maria Anders\",
\"ContactTitle\":\"Sales Representative\",
\"Address\":\"Obere Str. 57\",
\"City\":\"Berlin\",
\"Region\":null,
\"PostalCode\":\"12209\",
\"Country\":\"Germany\",
\"Phone\":\"030-0074321\",
\"Fax\":\"030-0076545\",
\"EntityState\":2,
\"EntityKey\":{\"EntitySetName\":\"Customers\",
\"EntityContainerName\":\"NorthwindEntities\",
\"EntityKeyValues\":[{\"Key\":\"CustomerID\",\"Value\":\"ALFKI\"}],\"IsTemporary\":false}},...


你能看到是一個相關完整的JSON,然後送給如jQuery處理。

ASP.NET MVC 3 新HTML Helper筆記(7) -- WebCache Class

在ASP.NET MVC 3之前,我們設定Cache (快取)都是透過 ActionFilter 的屬性來設定( <OutputCache()> ),但 ActionFilter 的屬性都是針對 Action (指動作)來進行快取,如果我們需要對「資料」做快取,我們就可以使用 WebCache類別

WebCache類別使用範例


HomeController.vb

Function Index() As ActionResult
    ' WebCache.Set(Key, Value, 存活時間, 滑動)
    WebCache.Set("ViewTime", DateTime.Now.ToString(), 1, False)
    Return View()
End Function

Value本身是Object,讓我們可以儲存資料。

在 About.aspx (View)中:

<%: WebCache.Get("ViewTime")%>
<% WebCache.Remove("ViewTime")%>

第一個Get,讓我們取出快取。第二個Remove,讓我們移除快取。

使用NuGet安裝外掛時的相依性問題

在使用NuGet安裝jQuery外掛時出現相依性問題,訊息如下:

PM> Install-Package jQuery
Successfully installed 'jQuery 1.6.1'.
Install failed. Rolling back...
Install-Package : Conflict occurred. 'jQuery 1.5.1' referenced but requested 'jQuery 1.6.1'. 'jQuery.vsdoc 1.5.1' depends on 'jQuery 1.5.1'.
位於 行:1 字元:16
+ Install-Package <<<<  jQuery
    + CategoryInfo          : NotSpecified: (:) [Install-Package], InvalidOperationException
    + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.Commands.InstallPackageCommand

之前NuGet是不會檢查相依性的,解決辦法是,進入「Add Library Package Reference」的「Installed packages」,然後移除相關相依性的packages,然後再安裝最新版本的packages即可。

另外也可以注意一下,專案根目錄下的packages.config,它會記錄目前NuGet所管理的packages狀況。

ASP.NET MVC 3 新HTML Helper筆記(6) -- WebMail Class

從前面看到這裡,你應該已經了解,這一個 System.Web.Helpers 類別,就是將一些常用的類別程式碼做再封裝的動作。接下來的這個 WebMail 類別也是一樣,讓我們透過簡單程式碼就能做 SMTP 的信件傳送。你可以與 System.Net.Mail 相互比較一下。

Private Sub SendRegisterMail(customer As Customers)
    ' SMTP Server
    WebMail.SmtpServer = "192.168.3.10"
    ' SMTP Server的使用者帳戶
    WebMail.UserName = "KKBruce"
    ' SMTP Server的使用者密碼
    WebMail.Password = "P@ssw0rd"

    WebMail.Send("收件者", "主題", "內容", "寄件者", "副本", "附加檔", "是否為HTML內容", "額外Header")
End Sub

你只要設定好SMTP Server相關參考,然後WebMail.Send()方法來將郵件傳送出去。

ASP.NET MVC 3 新HTML Helper筆記(5) -- ServerInfo Class

這個ServerInfo類別就是讓可以查看網頁伺服器相關變數值( IIS Server Variables ),就是 ASP 的 Request.ServerVariables 集合,或是 ASP.NET 的 ServerVariables 屬性,但以前必須很麻煩的自己寫 For 迴圈,現在直接使用即可。

ServerInfo只有一個方法,就是 GetHtml(),跟 WebGrid.GetHtml() 很像,就是回傳一個 Table Html。

Index.aspx (View)
<%: ServerInfo.GetHtml()%>

即可輸出完整ASP.NET 伺服器資訊。提醒你,如果需要使用,請小心安全性問題。

ASP.NET MVC 3 新HTML Helper筆記(4) -- Chart Class

有了WebImage類別的基礎後,在來看Chart類別比較輕鬆一些。

Chart類別是圖表類別,之前有介紹過「ASP.NET+jqPlot實作線上即時統計圖表(Chart)」是一樣的意思,最重要的是傳入「數值」然後產生圖表圖片,然後輸出。

在 HomeController.vb 新增一個Sub的Action
Sub GetChart()
    ' 建立一個 600 * 400,藍色樣式的圖表
    Dim demo = New Chart(600, 400, ChartTheme.Blue)
    ' 加入圖表資料
    demo.AddSeries("Demo Chart",
                   chartType:="bar",
                   legend:="Demo Chart",
                   xValue:=New String() {"一月", "二月", "三月", "四月", "五月"},
                   yValues:=New String() {"10", "20", "30", "40", "50"})
    ' 輸出為一個 image
    demo.Write()
End Sub


ChartTheme圖表樣式有:

  • Blue
  • Green
  • Vanilla
  • Vanilla3D
  • Yellow

Chart Type可到MSDN的「Chart Types (Chart Controls)」查詢,基本需求,Chart類別的圖表類型,應該都能滿足你了。

資料來源當然不可能用手Key-in,我們可以從「Displaying Data in a Chart」學到這項技術。

Windows 7與Sata 3晶片相容性問題

我的家用電腦舊HDD掛了一顆,所以買了一顆WD Caviar Blue 1TB EALX ( WD10EALX ),因為這顆HDD是SATA 3介面,而我的當初就是為了擴充性,所以多了些銀子買比較好的主機板 ( P7P55D-E ),這時候就派上用場。

原電腦是3顆WD 640GB HDD做Raid 5,壞了一顆,且這顆WD 640GB已經停產也買不到了。電腦是活的好好的,但常常用到一半就說「Disk 效能低落」,不然就是常常停止呼吸 ( 完全沒反應 ),然後過幾分鐘又活過來。

所以重新規劃:
  1. SATA 2:2 * WD 640GB 做 Raid 0;
  2. SATA 3:WD 1TB 安裝 Windows 7 x64,做系統碟。

你問我,用SATA 3裝Windows 7 x64,有沒有比較快。經婆婆實測,她說有。速度上,應該是乾淨的系統,所以速度上很不錯。(Windows 的分數HDD還是5.9)

但整體而言,系統不是很穩定,例如,瀏覽器 ( IE8, IE9, FF4 ) 常常會沒原因的自我關閉,程式常常跑一跑就Crash,連用隨身硬碟來複製資料,都能讓數年不見的藍白畫面跑出來見我好幾次。

我非常奇怪,似乎同樣的硬體(只差一顆HDD),怎麼跑起來穩定度差那麼多?還讓我重灌了二次Windows,結果都差不多,快是快,但就是不穩。

今天上網找看看是否有跟我一樣的人。結果找到Asus論壇的討論:
重點在第二篇,站長直接說出:「Marvell的SATA6G控制器是不建議當開機碟的。(有一些限制在)」、「Marvell 9123晶片組本身不支援安裝作業系統,一般會建議拿來接資料碟。」

看來要來安裝第三次Windows了。

第三次重新規劃:
  1. SATA 2:2 * WD 640GB 做 Raid 0 (麻煩活久一點),安裝 Windows 7 x64,做系統碟。
  2. SATA 3:WD 1TB 做資料碟。
這也是個經驗,SATA 3新是新,但必須注意晶版相容性問題,不過這種東西不會寫在網站上或手冊上,也只能等你碰到了,才有機會了解。XD

ASP.NET MVC 3 新HTML Helper筆記(3) -- WebImage Class

在ASP.NET MVC 3之前,對於<img />的支援並不多,如果網頁有大量的圖片要處理,通常會自行撰寫「擴充方法」來擴充HtmlHelper型別,而在ASP.NET MVC 3的WebImage類別就是要來補強這方面的不足,而且這個WebImage類別不單單只是產生一個<img />,更提供許多對於「圖片, Image」更全面的操控。

WebImage類別其提供8大功能:
  1. 取得圖片
  2. 取得WebImage的屬性
  3. 圖片的縮放
  4. 翻轉
  5. 浮水印
  6. 裁剪
  7. 儲存
  8. 輸出

取得圖片


' 上傳中取得圖片
Dim picture1 As WebImage = WebImage.GetImageFromRequest("postedFileName")
' 從String(Path)取得圖片
Dim picture2 As WebImage = New WebImage(Server.MapPath("~/Content/images/kingkong.jpg"))
' 從Stream取得圖片
Dim picture3 As WebImage = New WebImage(System.IO.File.OpenRead(Server.MapPath("~/Content/Images/kingkong.png")))
' 從Byte()取得圖片, 通常是Database,
Dim picture4 As WebImage = New WebImage(picture3.GetBytes())
' 複製取得圖片
Dim picture5 As WebImage = picture2.Clone()

取得WebImage的屬性


' 圖片名稱
picture2.FileName()
' 圖片高度
picture2.Height()
' 圖片格式,例如,JPEG, PNG ...
picture2.ImageFormat()
' 圖片寬度
picture2.Width()

圖片的縮放


picture2.Risize(寬,高,"等比例縮放", "不超過原圖大小")

翻轉


' 水平翻轉
picture2.FlipHorizontal()
' 垂直翻轉
picture2.FlipVertical()
' 左轉90度
picture2.RotateLeft()
' 右轉90度
picture2.RotateRight()

浮水印


' 文字浮水印
' AddTextWatermark("文字", "前景色", "文字大小", "字型樣式", "字型", "水平對齊", "垂直對齊", "透明度",  "邊距")
picture2.AddTextWatermark("KKBruce", "Black", 14, "Sign", "Arial", "Right", "Bottom", 60, 5)

水平對齊:

  • Left
  • Center
  • Right

垂直對齊:

  • NotSet (未設定)
  • Top
  • Middle
  • Bottom

透明度:0 (100% 透明, 即看不見)

' 圖片浮水印,差異只有第一個參數
' AddImageWatermark ("路徑", 寬, 高, "水平對齊", "垂直對齊", "透明度", "邊距")
' AddImageWatermark (WebImage , 寬, 高, "水平對齊", "垂直對齊", "透明度", "邊距")
picture2.AddImageWatermark(picture3, 100, 100, "Left", "Top", 50, 5)

裁剪


' Crop(上, 左, 下, 右),上左下右各要裁剪多少
picture2.Crop(50, 50, 50, 50)

儲存


' Save("路徑", "圖片格式", "是否使用imageFormat格式來儲存")
picture2.Save(Server.MapPath("~/Content/images"), "PNG", False)

輸出


' 把Picture2輸出為image
picture2.Write()

WebImage範例


以下範例修改自http://weblogs.asp.net/gunnarpeipman/archive/2010/10/30/asp-net-mvc-3-building-simple-image-editor-using-webimage-helper.aspx,新增一個指定圖片名稱。

1. 先在預設的 HomeController.vb 中新增一個Sub的Action

Sub GetImage(FileName As String,
             horizontalFlip As String,
             verticalFlip As String,
             rotateLeft As String,
             rotateRight As String)


    Dim FullPath As String = ""
    ' 是否有傳入圖片名稱
    If (Not String.IsNullOrWhiteSpace(FileName)) Then
        FullPath = "~/Content/images/" & FileName
    Else
        FullPath = "~/Content/images/kingkong.jpg"
    End If
    ' 取得圖片路徑
    Dim imagePath As String = Server.MapPath(FullPath)

    ' WebImage物件
    Dim image As WebImage = New WebImage(imagePath)

    ' 是否進行水平翻轉
    If (Not String.IsNullOrWhiteSpace(horizontalFlip)) Then
        image = image.FlipHorizontal()
    End If

    ' 是否進行垂直翻轉
    If (Not String.IsNullOrWhiteSpace(verticalFlip)) Then
        image = image.FlipVertical()
    End If

    ' 是否進行左90度翻轉
    If (Not String.IsNullOrWhiteSpace(rotateLeft)) Then
        image = image.RotateLeft()
    End If

    ' 是否進行右90度翻轉
    If (Not String.IsNullOrWhiteSpace(rotateRight)) Then
        image = image.RotateRight()
    End If

    ' 圖片輸出
    image.Write()
End Sub

在Index.aspx新增以下程式碼

<% Using (Html.BeginForm())%>
        <table border="0" cellpadding="0" cellspacing="5">
        <tr>
        <td valign="top" rowspan="10">
            <img src="/Home/GetImage"  id="image" alt="KingKong" />
        </td>
        <td valign="middle"><input type="checkbox" id="HorizontalFlip" value="1" />
            Flip horizontally</td>
        </tr>
        <tr>
        <td valign="middle"><input type="checkbox" id="VerticalFlip" value="true" />
            Flip vertically</td>
        </tr>
        <tr>
        <td valign="middle"><input type="checkbox" id="RotateLeft" value="true" />
            Rotate left</td>
        </tr>
        <tr>
        <td valign="middle"><input type="checkbox" id="RotateRight" value="true" />
            Rotate right</td>
        </tr>
        <tr>
        <td valign="middle">FileName:<input type="text" id="FileName" value="baby.jpg" /></td>
        </tr>
        <tr>
        <td><input type="button" value="Preview" onclick="updateImage()" /></td>
        </tr>
        </table>
<% End Using%>

引用jQuery,然後新增以下Javascript:

function updateImage() {
    var req = '/Home/GetImage?';
    var query = '';

    if ($('#FileName').val() != null) 
        query += '&FileName=' + $('#FileName').val();
    if ($('#HorizontalFlip').is(':checked'))
        query += '&horizontalFlip=1';
    if ($('#VerticalFlip').is(':checked'))
        query += '&verticalFlip=1';
    if ($('#RotateLeft').is(':checked'))
        query += '&rotateLeft=1';
    if ($('#RotateRight').is(':checked'))
        query += '&rotateRight=1';
        
    req += query.substr(1);
    $('#image').attr("src", req);
}

重點在<img src="/Home/GetImage" id="image" alt="KingKong" />這一行,我們透過jQuery傳入參數,動態改更src屬性,這樣我們就能針你所轉入的檔案名稱,進行對應的動作。例如,<img src="/Home/GetImage?FileName=baby.jpg&verticalFlip=1&rotateRight=1" id="image" alt="KingKong">。

[限制級] -- 性教育很重要

以下內容會有情色字眼,如不喜歡,請不要往下看。

前陣子,受朋友之託(女生),幫朋友的小孩上國中家教。我上數學,其他由婆婆負責。我常常要找空擋或利用假日寶貝們午休時間,我才有空自己先複習。以前不讀書,現在讓你讀個夠。@_@

我朋友早結婚(也早離婚),二位女兒,大女兒國中二年級,小女兒國小六年級。因為朋友忙於生意,所以照顧小孩這一塊就比較疏忽,她知道我們很會照顧小孩,其實是希望我們能帶她的小孩,走向正途。言外之意,就是有點走歪。不過還好,我們幫忙的這段時間裡,發現問題不大,其實就是沒有人陪伴照顧開導。反正,她的小孩很clever,成績不用說,用飛的、用衝的。(言外之意,本來飛很低)

有一天,婆婆上完家教回來,馬上就跟我說,現在的小孩怎麼那麼「恐怖」!
我說:「怎麼了」?
我以為是朋友的女兒出了什麼Trouble。
「朋友的大女兒,被男同學摸胸部。」婆婆說。
「而且一次是二個人。」婆婆說。
我說:「怎麼會這樣,老師怎麼處理?」
「老師有是男同學的家屬來。」婆婆說。
「不過,只是道歉。」婆婆說。
「妳朋友並不知道,她女兒不想跟她說。」婆婆說。

…(中間省略)

「Bruce,你知道嗎,她大女兒說,他們那些男生,上課都不上課,都把A片下載到手機裡,然後在上課邊看邊自衛打手槍。」婆婆說。
吞了口口水,然後我說:「我嚇到了。」
「然後她妹又接著說,她們國小的男生也會亂摸。如果是那種比較安靜的女生,只有被摸被欺負的份。」婆婆說。

…(省略)

事件是被摸,聊天是這些男生的行為。

科技本身無好壞,使用者會決定。記得我們的年代,電視、電影連有個Kiss畫面,就算是限制級,還有規定秒數。現在,任何資訊只要放到網路上,都會以飛快的速度散佈出去。管你限不限制,管你滿不滿18歲,反正Mouse Click什麼都看得到,什麼都抓得到。

性,是所有生物都必備的一種需求。記得,有看過這樣一句話:「人類是極少數可以不是為延續後代而有性行為的物種。」

在現代,性開放已是不爭的事實,所以我們更要做好孩子們的性教育,我們無知就算了,我們那個年級沒有什麼性知識,但現在時空不同了,不正確的性只會壞了孩子們的一生。

最近剛好在看一本「父母都是教育家」,這本書的內容我覺得適合每一個成年人看,而不只有父母親們。在Part 13一整篇都在討論與孩子的性教育問題。我很少看到有書願意討論教導人們關於性教育知識。

ASP.NET MVC 3 新HTML Helper筆記(2) -- Crypto Class

相較於WebGrid類別Crypto類別就簡單多了,它的功能就是產生 Hash 值 (雜湊值),在一個有「會員」的網站裡,Hash 的產生及比對是非常常使用的功能,「密碼」大概是使用Hash值的第一名。

我們在未使用Crypto類別前,必須手動撰寫程式碼:

''' <summary>
''' 密碼進行SHA1加密
''' </summary>
''' <param name="str">密碼</param>
''' <returns>SHA1加密後字串</returns>
''' <remarks></remarks>
Private Function HashPassword(str As String) As String
    Dim rethash As String = ""

    Dim hash As System.Security.Cryptography.SHA1 = System.Security.Cryptography.SHA1.Create()
    Dim encoder As New System.Text.ASCIIEncoding()
    Dim combined As Byte() = encoder.GetBytes(str)
    hash.ComputeHash(combined)
    rethash = Convert.ToBase64String(hash.Hash)
    Return rethash
End Function

現在我們改變需求,想要改使用MD5就好?後來又想,還是安全點好,又想改使用SHA256,改到最後,能不能讓我自己選擇加密演算法?

現在使用Crypto類別,把相關加密演算法再一次包裝,讓我們可以更方便就能使用這些加密演算法,使用方法很簡單:

' 依傳入的數值,產生亂數字串,預設值16
' 例如:tSea8lnjrMOXu6idzV3kmA==,每次產生會不一樣
ViewBag.GenerateSalt = Crypto.GenerateSalt()
' 預設加密演算法 sha256
' KKBruce加密後: D85385CBDE31076D8E4933D3D221373264D2904CF7AF97273A57C1989527C431 
ViewBag.Hash = Crypto.Hash("KKBruce")
' 指定加密演算法
' KKBruce加密後: 9C56ACC0AF06D1F7355048B54B8C256E
ViewBag.HashMD5 = Crypto.Hash("KKBruce", "md5")
' 使用 sha1SHA1 加密演算法
' KKBruce加密後: 897AD572561960D4A03B1EE128E03252A81A89C4
ViewBag.HashSHA1 = Crypto.SHA1("KKBruce")
' 等於使用 Crypto.Hash("String")
' KKBruce加密後: D85385CBDE31076D8E4933D3D221373264D2904CF7AF97273A57C1989527C431
ViewBag.HashSHA256 = Crypto.SHA256("KKBruce")
' 回傳base-64加密字串
' KKBruce加密後: AN4fRENuQOoRctTasprxrkM7ba+ixadD9MSY0aWFnn7hbx1mvFFQqvNlxNz2D1ya0w==
ViewBag.HashPassword = Crypto.HashPassword("KKBruce")

' 驗證Hash
Dim hashedPassword As String = Crypto.HashPassword("KKBruce")
' True
ViewBag.VerifyHashedPassword1 = Crypto.VerifyHashedPassword(hashedPassword, "KKBruce")
' False
ViewBag.VerifyHashedPassword2 = Crypto.VerifyHashedPassword(hashedPassword, "Bruce")

不管是加密或驗證密碼,透過Crypto類別幫忙之後簡化複雜度。

參考: