日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯網營銷解決方案
ASP.NETPage那點事

Page,我想每個ASP.NET開發(fā)人員對它應該都是比較熟悉的。

成都創(chuàng)新互聯2013年開創(chuàng)至今,先為瑯琊等服務建站,瑯琊等地企業(yè),進行企業(yè)商務咨詢服務。為瑯琊企業(yè)網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。

這次的博客我就打算專門談談它。不過呢,我不打算說 在Page中使用控件的一些話題,也不會說Page的生命周期的相關話題,因為我認為這些話題被人談論的次數實在是太多了,尤其是市面上的ASP.NET的書籍,都會比較喜歡這些話題。

我不喜歡重復,因此今天我只想談些人家不談的那點事,但我認為它們仍然很重要。

一些重要的Page指令

雖然Page公開了很多屬性,讓我們可以在運行時調整它的狀態(tài)與行為,但是,還有些重要的參數卻是以“指令”方式提供的,需要在設計時就指定。

下面是我整理的一些我認為 比較重要并且經常需要使用的指令:

#p#

web.config的全局設置

前面我介紹了一些常用的Page指令,考慮到方便性,ASP.NET還允許我們在web.config中為一些常用的指令配置默認值。下面我就一些常用的場景來說明這些全局配置的方便性。

1. 通常,我在創(chuàng)建一個網站項目時,肯定會決定不使用ViewState和Session的。那么如果為每個頁面設置EnableViewState,EnableSessionState指令屬性,那就顯得太麻煩了,而且還容易遺漏。此時,我們可以直接在web.config中為這些參數指定一個全局的默認值:

 
 
 
 

補充說明一下:全局禁用Session的徹底方法是把Session對應的HttpModule從httpModules列表中移除。

web.config允許我們設置Page默認參數的具體配置節(jié)如下:

 
 
 
 
  1.    buffer="[True|False]"
  2.    enableEventValidation="[True|False]"
  3.    enableSessionState="[True|False|ReadOnly]"
  4.    enableViewState="[True|False]"
  5.    enableViewStateMac="[True|False]"
  6.    smartNavigation="[True|False]"
  7.    autoEventWireup="[True|False]"
  8.    pageBaseType="typename, assembly"
  9.    userControlBaseType="typename"
  10.    validateRequest="[True|False]"
  11.    masterPageFile="file path" 
  12.    theme="string"
  13.    styleSheetTheme="string"
  14.    maxPageStateFieldLength="number" 
  15.    compilationMode="[Always|Auto|Never]" 
  16.    pageParserFilterType="string" 
  17.    viewStateEncryptionMode="[Always|Auto|Never]" 
  18.    maintainScrollPositionOnPostBack="[True|False]" 
  19.    asyncTimeout="number"
  20. >
  21.    ...
  22.    ...
  23.    ...

2. 為了代碼重用,設計用戶控件也是很常用的方法。

我們可以使用 @ Register指令 在頁面注冊需要使用的UserControl或者WebControl。然而,有些控件比較通用,許多頁面都會使用它,那么就不要再使用 @ Register指令了,可以在web.config中統(tǒng)一注冊。例如:

 
 
 
 
  1.     
  2.         
  3.         
  4.     

有了這個定義后,我就可以在任何頁面中直接使用:

 
 
 
 

3. 對于喜歡使用頁面內聯代碼的人來說,可能經常需要使用自己定義的類型。如果這些類型定義在某個命名空間中,那么就需要在內聯代碼中采用完整命名空間的寫法。雖然這樣做沒有什么問題,但就是麻煩,于是,我們可以在頁面中使用 @ Import指令 來導入我們需要使用的命名空間,但是這個指令每次只能導入一個命名空間,而且每個頁面還得重復導入,顯然不夠方便。

為了方便使用一些常用的命名空間,我們可以在web.config中統(tǒng)一指定,例如:

 
 
 
 
  1.     
  2.         
  3.         
  4.         
  5.     

這樣設置后,所有頁面就可以直接使用這些命名空間下的類型了。

不知道有些人想過:為什么在頁面中使用某些微軟提供的類型就不需要導入命名空間?

答案是:其實ASP.NET已經將一些微軟認為常用的命名空間在web.config中配置好了:

 
 
 
 
  1.     
  2.         
  3.         
  4.         
  5.         
  6.         
  7.         
  8.         
  9.         
  10.         
  11.         
  12.         
  13.         
  14.         
  15.         
  16.         
  17.     

4. 現在,有越來越多的人為了方便而使用擴展方法。使用擴展方法的好處是:可以讓我們不去關心這些擴展方法定義在那個類中,只要在支持擴展方法的對象上調用就可以了,就像下面的代碼這樣:

 
 
 
 
  1. 當前用戶已登錄,登錄名:<%= Context.User.Identity.Name.HtmlEncode() %>

然而,在頁面中使用擴展方法時,也必須先導入擴展方法的定義類的命名空間。

因此,為了方便,我們可以在web.config中為我們定義的擴展方法導入相應的命名空間:

 
 
 
 
  1.     
  2.         
  3.     

#p#

換個方式使用 Page

在傳統(tǒng)的WEB開發(fā)模式下,我們通常會設計一些頁面(Page)響應來自用戶瀏覽器的請求,在這種模式下,Page會將***生成的整頁HTML代碼直接發(fā)送給用戶瀏覽器。然而,在某些時候,我們只需要生成一個HTML片段:

1. 在AJAX請求中,客戶端為了局部刷新,只要求服務端返回一個HTML片段。

2. BigPipe方式下,為了能分塊輸出,每次也只需要輸出一個HTML片段。

如果只是為了得到一段簡單的HTML代碼,可能有些人會選擇采用代碼來拼接,但是如果那段HTML還有些復雜呢?顯然拼接方法肯定是不行的。

對于***個問題,可能有人說:我可以創(chuàng)建一個頁面,只放部分代碼到頁面上。的確,這種方法可以勉強解決***個問題,但是,很有可能那部分代碼在整頁輸出時也會用到,怎么辦?

做成UserControl,然后放在一個單獨的頁面中!其實這種做法很無奈,因為那個容器頁面的意義不大(僅僅是個容器),***搞得項目中一大堆頁面文件!事實上,這種方法僅適用于使用簡單服務端控件的場合,如果想使用一些高級的服務端控件,它根本就不行。

為了能實現前面說到的二個需求,我們就不能再按照傳統(tǒng)的方式來使用Page了。因為我們希望能得到(返回)一段HTML。

有二種方法可以讓我們繼續(xù)使用頁面模板代碼的方式生成HTML代碼:

1. Server.Execute()方法。

2. Page.RenderControl()方法。

下面這段代碼來源于 MyMVC框架,這個方法可以根據指定的用戶控件以及控件顯示所需的數據得到控件的輸出結果(一段HTML代碼)。

 
 
 
 
  1. /// 
  2. /// 用指定的用戶控件以及視圖數據呈現結果,***返回生成的HTML代碼。
  3. /// 用戶控件應從MyUserControlView繼承
  4. /// 
  5. /// 用戶控件的虛擬路徑
  6. /// 視圖數據
  7. /// 生成的HTML代碼
  8. public static string Render(string ucVirtualPath, object model)
  9. {
  10.     if( string.IsNullOrEmpty(ucVirtualPath) )
  11.         throw new ArgumentNullException("ucVirtualPath");
  12.     
  13.     Page page = new Page();
  14.     Control ctl = page.LoadControl(ucVirtualPath);
  15.     if( ctl == null )
  16.         throw new InvalidOperationException(
  17.             string.Format("指定的用戶控件 {0} 沒有找到。", ucVirtualPath));
  18.     if( model != null ) {
  19.         MyBaseUserControl myctl = ctl as MyBaseUserControl;
  20.         if( myctl != null )
  21.             myctl.SetModel(model);
  22.     }
  23.     // 將用戶控件放在Page容器中。
  24.     page.Controls.Add(ctl);
  25.     StringWriter output = new StringWriter();
  26.     HtmlTextWriter write = new HtmlTextWriter(output, string.Empty);
  27.     page.RenderControl(write);
  28.     // 用下面的方法也可以的。
  29.     //HttpContext.Current.Server.Execute(page, output, false);
  30.     return output.ToString();
  31. }

整段代碼分為以下幾個步驟(我已用空行分隔開了):

1. 檢查參數。

2. 創(chuàng)建頁面容器并加載用戶控件。

3. 設置頁面(視圖)所需的顯示數據。

4. 將用戶控件添加到Page的Controls集合中。

5. 調用RenderControl或者Execute讓Page輸出HTML代碼。

6. 返回結果。

這段代碼很簡單,唯獨值得介紹的就是第5步,調用它們就可以得到控件輸出的HTML代碼。

RenderControl或者Execute的差別在于:

RenderControl不支持服務器控件,原因在于它利用了頁面的一種獨特編譯方式,我已在以前的博客中分析過了。

Execute可以支持服務器控件,因為它會執(zhí)行一次完整的頁面生命周期。

注意:上面這段代碼就算使用Execute,也只能支持部分簡單的服務器控件,因為一些復雜的服務器控件需要在HtmlForm中才能運行。因此,如果需要支持所有的服務器控件,那么還必須創(chuàng)建HtmlForm對象,并調整包含關系,還有就是還需要去掉產生的多余HTML代碼。

如果你需要生成整個頁面生成的HTML代碼,可以參考 MyMVC框架,那里有實現這個功能的完整代碼。

#p#

重新認識Eval()方法

我想很多人都寫過類似下面的代碼:

 
 
 
 
  1.     
    •     
  2.     
  3.         
  4. <%# Eval("OrderID")%>,<%# Eval("OrderDate")%>,<%# Eval("SumMoney")%>
  5.         
  6.     

在這里我要說的是 Eval() 的調用,還不是Repeater控件。

Eval()不僅僅可以讀取一個綁定數據項的屬性,還可以去讀取DataTable中的一個數據列。而且還能完成更復雜的綁定計算:

 
 
 
 
  1. <%# Eval("OrderID")%>,<%# Eval("OrderDate")%>,<%# Eval("SumMoney")%>
  2.     ,訂單中的***個商品:<%# Eval("Detail[0].ProductName") %>

當然了,對于頁面上的數據綁定,用Eval()的確不是性能***的方法,建議還是使用強類型轉換的方法。

有時候,尤其是在寫反射應用時,時常會有從字符串解析并實現求值計算的需求。那么,前面這個示例中,Eval()的功能是不是值得挖掘呢?我認為答案是肯定的。

通過分析ASP.NET的綁定代碼,我發(fā)現Eval在內部會調用DataBinder.Eval這個靜態(tài)方法,這個方法的簽名如下:

 
 
 
 
  1. //     在運行時計算數據綁定表達式。
  2. //
  3. // 參數:
  4. //   container:
  5. //     表達式根據其進行計算的對象引用。此標識符必須是以頁的指定語言表示的有效對象標識符。
  6. //
  7. //   expression:
  8. //     從 container 到要放置在綁定控件屬性中的公共屬性值的導航路徑。
  9. //     此路徑必須是以點分隔的屬性或字段名稱字符串,如 C# 中的 "Tables[0].DefaultView.[0].Price"
  10. //     或 Visual Basic 中的 "Tables(0).DefaultView.(0).Price"。
  11. //
  12. // 返回結果:
  13. //     System.Object,它是數據綁定表達式的計算結果。
  14. public static object Eval(object container, string expression);

通過這個簽名的注釋,我們可以很容易地看出它的用法。

下面我來舉個例子把它應用在非綁定的應用中:

我有一個類:

 
 
 
 
  1. public class TestEvalClass
  2. {
  3.     public List Orders { get; set; }
  4.     // Order以及OrderDetail的定義就省略了,我想大家能想像得出來。
  5. }

那么下面的代碼是可以運行的:

 
 
 
 
  1. static void Main()
  2. {
  3.     TestEvalClass testObject = GetTestEvalClassInstance();
  4.     string productName = (string)System.Web.UI.DataBinder.Eval(testObject, "Orders[0].Detail[0].ProductName");
  5.     Console.WriteLine(productName);
  6. }

對于這個示例,我想輸出什么結果,并不重要。

我只想說:如果讓你去解析那個表達式,會不會比較麻煩,現在有現成的,用起來是不是很方便?

#p#

不用基類也能擴展

在一個ASP.NET網站中,如果想為所有的頁面添加某個功能,我們通常會想到使用基類的方式去實現。這的確是一種很有效的方法,但不并唯一的方法,還有一種方法也能容易實現這個需求,那就是使用PageAdapter的方式。

在我寫博客的過程中,我寫了很多示例頁面,頁面中包含一些提交按鈕是少不了的事情,然而,為了能讓示例代碼看起來比較原始(簡單),我盡量不使用服務器控件,因此就要面臨提交按鈕的事件處理問題。在博客【細說 ASP.NET Cache 及其高級用法】的示例代碼中,我開始采用PageAdapter這種方法,它可以讓代碼很簡單,而且以后也方便以后重用(只需要復制幾個文件即可)。

或許有些人認為:擴展所有頁面的功能,還是使用基類比較好。

對于這個觀點,我完全不反對。

但是,PageAdapter的好處在于它的可插拔性(類似HttpModule的優(yōu)點)。不過,我當時設計這種擴展方式只是想再換個方法嘗試一下而已。

其實微軟設計PageAdapter的本意是為了處理各種瀏覽器的兼容問題,但是我把這個功能用到擴展Page的功能上去了。 HttpModule可以進入到ASP.NET請求管線的任何階段,但它就是進入不了頁面的生命周期中,有了這個方法,我們就可以采用HttpModule這種【外掛】式的方法進入到頁面生命周期中,我認為是很有意義的。

方法多了,我想不是件壞事。每種方法都有適合它們的應用場合,了解更多的方法,以后就能做出更優(yōu)秀的設計。

這次想到這個話題是因為前面的博客【細說ASP.NET Forms 身份認證】中的示例代碼。有些人看到那些代碼,發(fā)現代碼的運行方式比較特別,所以,今天我就打算著重介紹這種方法。

我們再來回顧一下以前博客中的示例代碼,首先從頁面代碼開始:

 
 
 
 
  1. 普通登錄" method="post">
  2.     登錄名:
  3.     
  4.  
  5. 包含【用戶信息】的自定義登錄    " method="post">
  6.     
  7.     登錄名:
  8.         
  9.     UserId:
  10.         
  11.     GroupId:
  12.         
  13.         1表示管理員用戶
  14.         
  15.     用戶全名:
  16.         
  17.         
  18.     

在這段頁面代碼中,我定義了二個表單,它們包含各自的提交按鈕(其實這也只是部分代碼)。

再來看后臺處理代碼是如何響應提交請求的:

 
 
 
 
  1. public partial class _Default : System.Web.UI.Page 
  2. {
  3.     [SubmitMethod(AutoRedirect = true)]
  4.     public void NormalLogin()
  5.     {
  6.         // 省略登錄處理代碼。
  7.         // 如果需要知道這段代碼可以瀏覽下面的網址:
  8.         // http://www.cnblogs.com/fish-li/archive/2012/04/15/2450571.html 
  9.     }
  10.     [SubmitMethod(AutoRedirect = true)]
  11.     public void CustomizeLogin()
  12.     {
  13.         // 省略登錄處理代碼。
  14.         // 如果需要知道這段代碼可以瀏覽下面的網址:
  15.         // http://www.cnblogs.com/fish-li/archive/2012/04/15/2450571.html 
  16.     }

注意觀察,這二個C#方法的名稱與頁面二個submit按鈕的name屬性相同,因此可以猜測到這二個C#方法可以處理那二個submit按鈕的提交請求。那么這二段代碼是如何運行起來的呢?有些人或許看到了[SubmitMethod]的使用,認為與它們有關。其實這種說法并不正確,我也可以完全不使用它們。請記?。篈ttribute永遠只是一個標記,它不可能讓代碼自動運行起來。

前面的代碼能運行起來,與App_Browsers目錄下的Page.browser文件有關,此文件的代碼如下:

 
 
 
 
  1.     
  2.         
  3.             
  4.                      adapterType="FishDemoCodeLib.MyPageAdapter, FishDemoCodeLib" />
  5.         
  6.     

這里定義了一個MyPageAdapter,它用于Page控件的請求過程。 refID="Default" 表示是對ASP.NET定義的Default.browser文件補充一些配置,它將能匹配來自所有瀏覽器的請求。

我再來看一下MyPageAdapter的代碼:

 
 
 
 
  1. [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
  2. public class SubmitMethodAttribute : Attribute
  3. {
  4.     public bool AutoRedirect { get; set; }
  5. }
  6. internal sealed class MethodInvokeInfo
  7. {
  8.     public MethodInfo MethodInfo;
  9.     public SubmitMethodAttribute MethodAttribute;
  10. }
  11. public class MyPageAdapter : System.Web.UI.Adapters.PageAdapter
  12. {
  13.     private static readonly Hashtable s_table = Hashtable.Synchronized(new Hashtable());
  14.     private static MethodInvokeInfo[] GetMethodInfo(Type type)
  15.     {
  16.         MethodInvokeInfo[] array = s_table[type.AssemblyQualifiedName] as MethodInvokeInfo[];
  17.         if( array == null ) {
  18.             array = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
  19.                      let a = m.GetCustomAttributes(
  20.                             typeof(SubmitMethodAttribute), false) as SubmitMethodAttribute[]
  21.                      where a.Length > 0
  22.                      select new MethodInvokeInfo { 
  23.                             MethodInfo = m, MethodAttribute = a[0] }).ToArray();
  24.             s_table[type.ToString()] = array;
  25.         }
  26.         return array;
  27.     }
  28.     protected override void OnLoad(EventArgs e)
  29.     {
  30.         base.OnLoad(e);
  31.         if( Page.Request.Form.AllKeys.Length == 0 )
  32.             return;    // 沒有提交表單
  33.         MethodInvokeInfo[] array = GetMethodInfo(Page.GetType().BaseType);
  34.         if( array.Length == 0 )
  35.             return;
  36.         foreach( MethodInvokeInfo m in array ) {
  37.             if( string.IsNullOrEmpty(Page.Request.Form[m.MethodInfo.Name]) == false ) {
  38.                 m.MethodInfo.Invoke(Page, null);
  39.                 if( m.MethodAttribute.AutoRedirect 
  40.                                 && Page.Response.IsRequestBeingRedirected == false )
  41.                     Page.Response.Redirect(Page.Request.RawUrl);
  42.                 return;
  43.             }
  44.         }
  45.     }
  46. }

這段代碼并不長,核心代碼更是比較少。

代碼中,最重要的一塊是MyPageAdapter的實現,它繼承了System.Web.UI.Adapters.PageAdapter,并重寫了OnLoad方法(相當是在重寫Page的OnLoad方法),也正是由于這個重寫,代碼才有機會在頁面的生命周期中被執(zhí)行,這一點是HttpModule做不到的。

在OnLoad方法中做了以下事情:

1. 檢查是不是發(fā)生了表單提交的操作。

2. 獲取當前頁面類型的所有[SubmitMethod]修飾過的方法。

3. 檢查提交的表單數據中,是否存在與name對應的C#方法名。

4. 如果找到一個匹配的方法名,則調用。

5. 如果在[SubmitMethod]中設置了AutoRedirect=true,則引發(fā)重定向。

注意:如果不調用base.OnLoad(e); 那么頁面的Load事件根本不會發(fā)生。也就是說:PageAdapter.OnLoad的調用時間要早于Page.Onload方法。

由于這段代碼僅供我寫示例代碼時使用,因此并沒有檢查要調用的方法的參數是否滿足條件,也沒有優(yōu)化刻意去優(yōu)化它的性能。在我的設計中,被調用的方法應該是無參的,因此是容易判斷的,而且可以使用一個固定簽名的委托去優(yōu)化它的,這些細節(jié)留著以后再去完善它吧!


新聞名稱:ASP.NETPage那點事
網站路徑:http://m.5511xx.com/article/cdieoos.html