寫TemplatedControl時,ASP.NET 2.0有個好用的新函數Eval("FieldName"),可以在DataGrid、GridView等物件的Template Column中,將DataItem中某個欄位的值指定給Label、TextBox之類的;在ASP.NET 1.1時代,這得寫成DataBinder.Eval(Container, "DataItem.FieldName"),相較之下,新寫法簡潔多了! 我查到大陸朋友寫的一篇精彩文章(同一篇文章被轉貼得到處都是[Goggle共找到1350處 orz],但幾乎都沒註明原出處,只好隨便貼一個URL給大家參考,並向不知名的原作者致上敬意),用Reflection的技巧去追出其實Eval("FieldName")內部呼叫了DataBinder.Eval(this.Page.GetDataItem(),  "FieldName"),文中甚至還探討了不同做法的效能,這下Eval函數的原理就很清楚了。

Eval有個好兄弟叫Bind,用在雙向的Binding上,例如EditItemTemplate中,需將User在TextBox輸入的內容回寫到DataItem。

同事小娟問了一個有趣的問題,她發現在DataBinding中呼叫自訂函數時,<%# MyFunc(Eval("FieldName") %>的寫法可以過關,但<%# MyFunc(Bind("FieldName") %>卻會傳回
Compiler Error Message: CS0103: The name 'Bind' does not exist in the current context

我認為這是因為Data-Binding Syntax裡,並不是直接將整段內容當成Source Code拿來Compile,而是進行了一些Parsing及轉換。但推論歸推論,要怎麼證明呢?

   1:      <form id="form1" runat="server">
   2:      <div>
   3:      <asp:GridView ID="GridView1" runat="server" 
   4:          AutoGenerateColumns="False">
   5:          <Columns>
   6:              <asp:TemplateField HeaderText="NameEval">
   7:                  <ItemTemplate>
   8:                      <asp:Label ID="Label1" runat="server" 
   9:                      Text='<%# Eval("Name_Eval") %>'></asp:Label>
  10:                  </ItemTemplate>
  11:              </asp:TemplateField>
  12:              <asp:TemplateField HeaderText="NameBind">
  13:                  <ItemTemplate>
  14:                      <asp:Label ID="Label2" runat="server" 
  15:                      Text='<%# Bind("Name_Bind") %>'></asp:Label>
  16:                  </ItemTemplate>
  17:              </asp:TemplateField>
  18:              <asp:TemplateField HeaderText="NameFuncEval">
  19:                  <ItemTemplate>
  20:                      <asp:Label ID="Label3" runat="server" 
  21:       Text='<%# CvrtStr(Eval("Name_Func_Eval")) %>'></asp:Label>
  22:                  </ItemTemplate>
  23:              </asp:TemplateField>
  24:              <asp:TemplateField HeaderText="NameFuncBind">
  25:                  <ItemTemplate>
  26:                      <asp:Label ID="Label4" runat="server" 
            Text='<%# CvrtStr(Bind("Name_Func_Bind")) %>'></asp:Label>
  28:                  </ItemTemplate>
  29:              </asp:TemplateField>            
  30:              
  31:          </Columns>
  32:      </asp:GridView>
  33:      </div>
  34:  
  35:      </form>  

以上的程式碼是跑不動的,你會得到前述找不到Bind的錯誤,但錯誤頁面本身其實已提供了線索,只是常被忽略而已。我們都知道ASPX的Code最後也會被Parsing成.cs/.vb即時Compile再與Code-Beside/Code-Behind的DLL一起整合執行,當ASPX端程式編譯失敗時,下方會出現:

Show Detail Compiler Output:

Show Complete Compilation Source:

按下Show Complete Compilation Source,就可以看到ASPX怎麼變成一個複雜的.cs。Trace其中,可以找到前面程式中四個Binding分別被轉成:

1.Eval("Name_Eval")

dataBindingExpressionBuilderTarget.Text = System.Convert.ToString( Eval("Name_Eval") , System.Globalization.CultureInfo.CurrentCulture);

2.Bind("Name_Bind")

dataBindingExpressionBuilderTarget.Text = System.Convert.ToString(this.Eval("Name_Bind"), System.Globalization.CultureInfo.CurrentCulture);

3.CvrtStr(Eval("Name_Func_Eval"))

dataBindingExpressionBuilderTarget.Text = System.Convert.ToString( CvrtStr(Eval("Name_Func_Eval")) , System.Globalization.CultureInfo.CurrentCulture);

4.CvrtStr(Bind("Name_Func_Bind")

dataBindingExpressionBuilderTarget.Text = System.Convert.ToString( CvrtStr(Bind("Name_Func_Bind")) , System.Globalization.CultureInfo.CurrentCulture);

答案揭曉了,原來根本不存在Bind這個Method,當我們寫Bind時,實際上它會被轉成this.Eval("FieldName"),而當它被包成自訂函數CvrtStr的參數時,則不會被解讀,而保持Bind(..)的寫法,導致Bind函數不存在的Build Error!

第一個迷團解開了,但如果Bind與Eval的差別只在於有沒有加this.並不合情理,如文件所說,Bind主要用來雙向的Data-Binding,所以應該會與Eval有明顯不同才對。仔細再挖下去,在無法Build的ASPX Source Code中,我們可以找到另一段Code:

  public System.Collections.Specialized.IOrderedDictionary 
    @__ExtractValues__control8(System.Web.UI.Control @__container) 
  {
      System.Collections.Specialized.OrderedDictionary @__table;
      System.Web.UI.WebControls.TextBox TextBox1;
      
      #line 13 "C:\WWW\TestWeb\BindTest.aspx"
      TextBox1 = ((System.Web.UI.WebControls.TextBox)
    (@__container.FindControl("TextBox1")));
      
      #line default
      #line hidden
      
      #line 13 "C:\WWW\TestWeb\BindTest.aspx"
      @__table = 
    new System.Collections.Specialized.OrderedDictionary();
      
      #line default
      #line hidden
      
      #line 13 "C:\WWW\TestWeb\BindTest.aspx"
      if ((TextBox1 != null)) {
          @__table["Name_Bind"] = TextBox1.Text;
      }
      
      #line default
      #line hidden
      return @__table;
  }

Bingo!! 這就是Bind與Eval的最大差別!!

當ASP.NET在Parsing Data-Binding Syntax時,遇到了Bind,除了加上設定TextBox.Text的程式碼,還會加上將TextBox.Text存入OrderedDictionary的邏輯,換句話說,之前的推論沒有錯: Bind算是Syntax,而非特定的Method。

真相大白,收工!


Comments

Be the first to post a comment

Post a comment