2008-09-26

The ParseChildren PersistChildren and PersistenceMode.InnerProperty

After a while of non-web control development, ParseChildren andPersistChildren attributes are important attributes to remember when trying to get the desired result’s your looking for in the Visual Studio designer.
So, this post should clear up (and serve as a reminder for me) how and why these attributes are important.  Let’s explore what these two attributes are used for.  Let’s start with ParseChildren.

The [ParseChildrenAttribute]

The ParseChildren Attribute is probably, the most important attribute you should pay attention to when developing web controls.  It’s actually used by the ASP.NET Parser and ControlBuilder object to figure out how to parse the ASP.NET code you write.  Visual Studio also uses this attribute to figure out what valid sub-controls and components are allowed within the contents of a server control.
Let’s say, I want to create an AggregateFeeds control that displays an aggregate list of RSS feeds.

A Basic and Boring Control Syntax

image

You’ll notice that the RssResource is the only available option that is allowed as a child from the AggregateFeeds control.  Here’s the code behind theAggregateFeeds control:
  113     [
  114     ParseChildren(
  115         typeof(RssResource),
  116         DefaultProperty = "Feeds",
  117         ChildrenAsProperties = true
  118         )
  119     ]
  120     public class AggregateFeeds : Control
  121     {
  122         public AggregateFeeds()
  123         {
  124             this.Feeds = new RssFeedCollection();
  125         }
  126         public RssFeedCollection Feeds
  127         {
  128             get;
  129             private set;
  130         }
  131         protected override void Render(HtmlTextWriterwriter)
  132         {
  133             this.Feeds
  134                 .ForEach( rssRes => writer.Write( rssRes.Url ) );
  135         }
  136     }
  137 
  138     public class RssFeedCollection : List<RssResource>
  139     {
  140 
  141     }
  142 
  143     public class RssResource
  144     {
  145         public string Url { getset; }
  146     }
The ParseChildren attribute on AggregateFeeds tells the ASP.NET, that any children within the AggregateFeeds control should betypeof(RssResource).  ChildrenAsProperties=true let’s ASP.NET know that it should STOP parsing server controls with "runat=server", and switch to instantiating objects into the properties of the ArggregateFeeds control. DefaultProperty says, that the results of the parsed objects should go into the default property Feeds.

Syntax Goodness With InnerProperty

The previous example was great, it’s simple and get’s the job done.  But let’s say, the requirements have changed, our control is growing, and we need to allow more customization, and extensibility for the consumers of ourAggregateFeeds control.
Let’s clean up the markup and allow our developers to create markup like this:
image
To get this type of syntactical behavior, check out the code below:
  113     [
  114     ParseChildren(
  115         ChildrenAsProperties = true
  116         )
  117     ]
  118     public class AggregateFeeds : Control
  119     {
  120         public AggregateFeeds()
  121         {
  122             this.Feeds = new RssFeedCollection();
  123         }
  124 
  125         [PersistenceMode(PersistenceMode.InnerProperty)]
  126         public RssFeedCollection Feeds
  127         {
  128             get;
  129             private set;
  130         }
  131         [PersistenceMode(PersistenceMode.InnerProperty)]
  132         public AggregateSettings Settings
  133         {
  134             get;
  135             private set;
  136         }
  137         protected override void Render(HtmlTextWriterwriter)
  138         {
  139             this.Feeds
  140                 .ForEach( rssRes => writer.Write( rssRes.Url ) );
  141         }
  142 
  143     }
  144 
  145     public class AggregateSettings
  146     {
  147         public int TimeOut { getset; }
  148         public bool CacheResults { getset; }
  149     }
  150 
  151     public class RssFeedCollection : List<RssResource>
  152     {
  153 
  154     }
  155 
  156     public class RssResource
  157     {
  158         public string Url { getset; }
  159     }
Notice, we’ve removed DefaultProperty and typeof(RssResource) fromParseChildren attribute.  We’re no longer working with a simple control that has simple children objects that need to be parsed, we’re now working with a complex control with more than one property that we’re setting in the markup, so we’ve removed the "default" stuff.  The syntactical magic happens withPersitanceMode attribute on the properties. PersistanceMode.InnerProperty allows us to specify our cool <Feeds>and <Settings> tags.  How does Visual Studio know what members are available?  It does so by Reflection.

Get fancy, more than one child type

Also, I want to point out, suppose, we want to support multiple types of Feed objects.  We could use an enum in RssResource, or we could use inheritance to achieve the following:
image
All we would have to do is simply mark RssResource as an abstract class. Then, subclass for each type.
  156     public abstract class RssResource
  157     {
  158         public string Url { getset; }
  159     }
  160     public class MediaRss : RssResource
  161     {
  162 
  163     }
  164     public class ITunesRss : RssResource
  165     {
  166 
  167     }
Again, I’m just showing that it’s possible, but following my mantra of "less code, less maintenance," I’d use an enum to describe the type of rss feed onRssResource.

Where is [PersistChildren]?

Nowhere!  Is PersistChildren attribute needed?  No, it’s not a required attribute to create your custom control.  The PersistChildrenAttribute only provides designer support for your control with Visual Studio and has no "processing" affect in ASP.NET, but remember ParseChildren does.
I’m a image  source-view only guy.  I really don’t remember the last time I’ve used the Visual Studio "Design View", it’s a waste, crashes all the time, so I’ve pretty much given up on it.  Besides, "Design View" is for n00bs anyway.  Just kidding!  If you plan on using the Design View, then you’ll probably need your PersistChildren attribute…
In general, PersistChildren and ParseChildren are exclusive complementary attributes to describe the same semantic operation.  The rule of thumb goes:
If ParseChildren(true), then PersistChildren(false)
If ParseChildren(false), then PersistChildren(true).
Following the PersistChildren guideline above should keep your code out of trouble.  But again, I wouldn’t use PersistChildren only until you actually need it.  Less code, less maintenance.
Here’s a nice list of attributes you should consider when writing your custom controls:
Hope that helps! Happy coding!
Brian Chavez


My Testing Codes:
<ParseChildren(True)> _
Partial Class MyControl_MyFrame_01
 Inherits System.Web.UI.UserControl
 Private _ContentTemplate As ITemplate = Nothing
 <PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(GetType(TemplateControl))> _
 Public Property ContentTemplate() As ITemplate
  Get
   Return _ContentTemplate
  End Get
  Set(ByVal value As ITemplate)
   _ContentTemplate = value
  End Set
 End Property
 Public Property Width() As String
  Get
   Return tb.Width
  End Get
  Set(ByVal value As String)
   tb.Width = value
  End Set
 End Property
 Public Property Title() As String
  Get
   Return TheTitle.Text
  End Get
  Set(ByVal value As String)
   TheTitle.Text = value
  End Set
 End Property
 Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
  MyBase.OnInit(e)
  If Not IsNothing(_ContentTemplate) Then _ContentTemplate.InstantiateIn(ContentPH)
 End Sub
End Class
 
如此在web引用時, 可用以下的作法
<MyUC:MyFrame_01 ID="test" runat="server" Width="100%" Title="123123" >         
          <ContentTemplate>
           <asp:label ID="DD" runat="server" Text="this is a label control"></asp:label>
          </ContentTemplate>
         </MyUC:MyFrame_01>
 
ContentTemplate裏的控制項會被包進uc裏的PlaceHolder控制項
<%@ Control Language="VB" AutoEventWireup="false" CodeFile="MyFrame_01.ascx.vb" Inherits="MyControl_MyFrame_01" %>
<table id="tb" runat="server" width="" border="0" cellpadding="0" cellspacing="0">
 <tr>
  <td height="30" background="../images/frame_images/bkg.jpg" align="center">
   <asp:Label ID="TheTitle" runat="server" Text="" CssClass="FrameTitleStyle"></asp:Label>
  </td>
 </tr>
 <tr>
  <td class="Border_01">  
   <asp:PlaceHolder ID="ContentPH" runat="server" EnableViewState="true"></asp:PlaceHolder>
  </td>
 </tr>
</table>

2008-09-01

Windows 2003 核發自己的 IIS SSL 憑證步驟

有時候開發有關SSL的網頁時,需要安裝憑證才能夠執行,但是開發階段又不可能去申請個正式憑證來安裝(況且申請憑證是要錢的)。在這樣的狀況下,如果您開發的作業系統是Windows 2003的話,可以安裝憑證服務,然後自己核發一份憑證給自己,這樣就能夠進行相關程式的開發了。

設定IIS的SSL

SSL的設定需要有憑證,憑證可以向CA申請 (如 HiTrust 的Verisign SSL ) 或自行建置,以下說明自行建置CA,核發憑證的過程

安裝憑證授權單位(CA)

  • 因為不想花錢或是只在內部使用的Web 站台,想要使用SSL的功能,就必須自己成為憑證授權單位,自己發憑證給自己的網站使用
  • 電腦名稱與CA有很大的關連,建議先將電腦名稱更改為叫有意義的名字
  • 先想好要IIS中的哪一個站台成為SSL的站台
  • 安裝CA的步驟:控制台/新增或移除程式/新增移除Windows元件/Certificate Services
  • 安裝完畢後會在IIS的預設的網站中建立CertSrv,CertControl,CertEnroll等三個虛擬目錄

產生憑證要求

  1. 開啟 IIS 管理員。
  2. 在要安裝憑證的網站按一下滑鼠右鍵,再按內容。 //是網站而不是虛擬目錄喔
  3. 按一下「目錄安全設定」索引標籤。
  4. 按一下伺服器憑證按鈕,按下一步
  5. 點選「建立新憑證」,按下一步,點選「準備要求,但於稍後傳送」,按下一步
  6. 輸入憑證名稱與金鑰位元長度,可採預設值即可
  7. 輸入公司名稱及單位,通常是公司與部門的正式名稱
  8. 輸入「一般名稱」,此名稱十分重要,預設為NetBios的電腦名稱(IntraNet上使用),但若使用在 InterNet上,則必須改用網站的FQDN名稱(例如:dcicc.ncit.edu.tw)。 //若網站名稱與憑證名稱不同,則使用者在瀏覽時會出現「安全性憑證的名稱不正確或與網站的名稱不相符」的安全性警告
  9. 在「國家/地區」,「省/州」與「城市/位置」欄位中輸入適當的資訊,再按下一步。
  10. 輸入欲產生的「憑證要求」檔案名稱 (c:\certreq.txt),再按下一步。
  11. —–BEGIN NEW CERTIFICATE REQUEST—–
    MIIDOjCCAqMCAQAwXzELMAkGA1UEBhMCVFcxDzANBgNVBAgTBlRhaXdhbjERMA8G
    A1UEBxMIVGFpY2h1bmcxDTALBgNVBAoTBGN5dXQxCzAJBgNVBAsTAmNjMRAwDgYD
    VQQDEwdlZHVmdW5kMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtS/DzjQiA
    5jymWsTZys9Y5gIGRAqFZrkROzRyE/gw0ScW8AV7idz2j+1Z24N9iSDO9Aixi9sG
    BQLQnUpftjDa0VTvycF7XnV/9ljKDUAVRatXYcBVQsnY8jEn+KrVr8x4UP3EvKtO
    ROfTxo728vPf4+QXSwOKUyVBXmTlbZD3VwIDAQABoIIBmTAaBgorBgEEAYI3DQID
    MQwWCjUuMi4zNzkwLjIwewYKKwYBBAGCNwIBDjFtMGswDgYDVR0PAQH/BAQDAgTw
    MEQGCSqGSIb3DQEJDwQ3MDUwDgYIKoZIhvcNAwICAgCAMA4GCCqGSIb3DQMEAgIA
    gDAHBgUrDgMCBzAKBggqhkiG9w0DBzATBgNVHSUEDDAKBggrBgEFBQcDATCB/QYK
    KwYBBAGCNw0CAjGB7jCB6wIBAR5aAE0AaQBjAHIAbwBzAG8AZgB0ACAAUgBTAEEA
    IABTAEMAaABhAG4AbgBlAGwAIABDAHIAeQBwAHQAbwBnAHIAYQBwAGgAaQBjACAA
    UAByAG8AdgBpAGQAZQByA4GJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAwDQYJKoZIhvcNAQEFBQADgYEAAQlm4lzDo/mJ/Ktg9ajKYF+B
    Rgjt1AxaWGG2BWMd1CjkOyiFR8jKnb1PTUWdBvTs+occHqkLYZobytne+/amrt6J
    zi7yDNCdxhpbkRi8t5Yx27R/0xK9gP+5/hJXeCXxhrucMEaCx+Au9QMWoCnUQ0jC
    McsN9//3bUA+1Xmn0rw=
    —–END NEW CERTIFICATE REQUEST—–
    檢視憑證要求檔案中的摘要,若沒有問題再按下一步。
  12. 按完成,結束產生「憑證要求檔案」的過程

送出憑證要求

  1. 將上一個步驟所產生的檔案c:\certreq.txt,其內容複製到剪貼簿
  2. 開啟瀏覽器,鍵入「http://HOSTNAME/CertSrv」 //HOSTNAME為CA的電腦名稱,若為本機也可設定為127.0.0.1
  3. 按一下「要求憑證」。
  4. 按一下「提交進階憑證要求」。
  5. 按一下「用 Base-64 編碼的 CMC 或 PKCS #10 檔案來提交憑證要求,或用 Base-64 編碼的 PKCS #7 檔案提交更新要求」。
  6. 在已儲存的要求下方的文字方塊中,按下Ctrl+V,貼上之前複製的內容,按一下「提交」。

發行憑證

  1. 控制台/系統管理工具/憑證授權單位
  2. 展開後點選「擱置要求」資料夾
  3. 點選剛剛提交的憑證要求
  4. 點選功能表上的「執行/所有工作/發行」
  5. 點選「發出的憑證」資料夾
  6. 對剛剛發出的憑證按2下滑鼠以檢視之
  7. 按下「詳細資料」的索引標籤,按一下「複製到檔案」
  8. 按下一步,點選匯出的檔案格式為「Base-64 Encoded X.509 (.CER)」
  9. 輸入欲產生的憑證檔案名稱,例如:C:\dcicc.cer
  10. 下一步/完成/確定。

在Web Server上安裝憑證

  1. 開啟 IIS 管理員。
  2. 在有憑證要求的網站上按下滑鼠右鍵,再按內容。
  3. 按一下「目錄安全設定」索引標籤。
  4. 按一下伺服器憑證按鈕,按下一步。
  5. 點選「處理擱置要求及安裝憑證」,再按下一步。
  6. 輸入CA回應之「憑證授權的檔案」的路徑與名稱,可採預設值即可(C:\dcicc.cer),再按下一步。
  7. 輸入網站使用的SSL連接埠,可採預設值443即可。
  8. 下一步/完成/確定。