日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁編程開發(fā)ASP.NET → ASP.NET中不用try/catch進(jìn)行正常處理異?偨Y(jié)

      ASP.NET中不用try/catch進(jìn)行正常處理異?偨Y(jié)

      相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來源:西西整理時(shí)間:2012/10/28 20:26:16字體大小:A-A+

      作者:西西點(diǎn)擊:214次評(píng)論:0次標(biāo)簽: 異常

      對(duì)于企業(yè)應(yīng)用的開發(fā)者來說,異常處理是一件既簡(jiǎn)單又復(fù)雜的事情。說其簡(jiǎn)單,是因?yàn)橄嚓P(guān)的編程無外乎try/catch/finally+throw而已;說其復(fù)雜,是因?yàn)槲覀兺茈y按照我們真正需要的策略來處理異常。我一直有這樣的想法,理想的企業(yè)應(yīng)用開發(fā)中應(yīng)該盡量讓框架來完成對(duì)異常的處理,最終的開發(fā)人員在大部分的情況下無需編寫異常處理相關(guān)的任何代碼。在這篇文章中我們將提供一個(gè)解決方案來讓ASP.NET應(yīng)用利用EntLib的異常處理模塊來實(shí)現(xiàn)自動(dòng)化的異常處理。

      源代碼:
      Sample1[通過重寫Page的OnLoad和OnRaisePostBackEvent方法]
      Sample2[通過自動(dòng)封裝注冊(cè)的EventHandler]

      一、EntLib的異常處理方式
      二、實(shí)例演示
      三、通過重寫Page的OnLoad和RaisePostBackEvent方法實(shí)現(xiàn)自動(dòng)異常處理
      四、IPostBackDataHandler
      五、EventHandlerWraper
      六、對(duì)控件注冊(cè)事件的自動(dòng)封裝
      七、AlertHandler

      一、EntLib的異常處理方式

      所謂異常,其本意就是超出預(yù)期的錯(cuò)誤。既然如此,異常處理的策略就不可能一成不變,我們不可能在開發(fā)階段就制定一個(gè)完備的異常處理策略來處理未來發(fā)生的所有異常。異常處理策略應(yīng)該是可配置的,能夠隨時(shí)進(jìn)行動(dòng)態(tài)改變的。就此而言,微軟的企業(yè)庫(以下簡(jiǎn)稱EntLib)的異常處理應(yīng)用塊(Exception Handling Application Block)是一個(gè)不錯(cuò)的異常處理框架,它運(yùn)行我們通過配置文件來定義針對(duì)具體異常類型的處理策略。

      針對(duì)EntLib的異常處理應(yīng)用塊采用非常簡(jiǎn)單的編程方式,我們只需要按照如下的方式捕捉拋出的一場(chǎng),并通過調(diào)用ExceptionPolicy的HandleException根據(jù)指定的異常策略進(jìn)行處理即可。對(duì)于ASP.NET應(yīng)用來說,我們可以注冊(cè)HttpApplication的Error事件的形式來進(jìn)行統(tǒng)一的異常處理。但是在很多情況下,我們往往需要將異常控制在當(dāng)前頁面之內(nèi)(比如當(dāng)前頁面被正常呈現(xiàn),并通過執(zhí)行一段JavaScript探出一個(gè)對(duì)話框顯示錯(cuò)誤消息),我們往往需要將下面這段相同的代碼結(jié)構(gòu)置于所有控件的注冊(cè)事件之中。

         1: try
         2: {
         3:     //業(yè)務(wù)代碼
         4: }
         5: catch(Exception ex)
         6: {
         7:     if(ExceptionPolicy.HandleException(ex,"exceptionPolcyName"))
         8:     {
         9:         throw;
        10:     }
        11: }

      我個(gè)人不太能夠容忍完全相同的代碼到處出現(xiàn),代碼應(yīng)該盡可能地重用,而不是重復(fù)。接下來我們就來討論如何采用一些編程上的手段或者技巧來讓開發(fā)人員無須編寫任何的異常處理代碼,而拋出的確卻能按照我們預(yù)先指定的策略被處理。

      二、實(shí)例演示

      為了讓讀者對(duì)“自動(dòng)化異常處理”有一個(gè)直觀的認(rèn)識(shí),我們來做一個(gè)簡(jiǎn)單的實(shí)例演示。我們的異常處理策略很簡(jiǎn)單:如果后臺(tái)代碼拋出異常,異常的相關(guān)信息按照預(yù)定義的格式通過Alert的方式顯示在當(dāng)前頁面中。如下所示的是異常處理策略在配置文件中的定義,該配置中定義了唯一個(gè)名為“default”的異常策略,該策略利用自定義的AlertHandler來顯示一場(chǎng)信息。配置屬性messageTemplate定義了一個(gè)模板用于控制顯示消息的格式。

         1: 
         2:   ...
         3:   
         4:     
         5:       
         6:         
         7:           <add type="System.Exception, mscorlib" 
         8:                   postHandlingAction="None" name="Exception">
         9:             
        10:               <add name="Alert Handler" type="AutomaticExceptionHandling.AlertHandler, AutomaticExceptionHandling" 
        11:                    messageTemplate="[{ExceptionType}]{Message}"/>
        12:             
        13:                     
        14:         
        15:       
        16:     
        17:        
        18: 

      現(xiàn)在我們定義一個(gè)簡(jiǎn)單的頁面來模式自動(dòng)化異常處理,這個(gè)頁面是一個(gè)用于進(jìn)行除法預(yù)算的計(jì)算器。如下所示的該頁面的后臺(tái)代碼,可以看出它沒有直接繼承自Page,而是繼承自我們自定義的基類PageBase,所有異常處理的機(jī)制就實(shí)現(xiàn)在此。Page_Load方法收集以QueryString方式提供的操作數(shù),并轉(zhuǎn)化成整數(shù)進(jìn)行除法預(yù)算,最后將運(yùn)算結(jié)果顯示在表示結(jié)果的文本框中。計(jì)算按鈕的Click事件處理方法根據(jù)用戶輸入的操作數(shù)進(jìn)行除法運(yùn)算。兩個(gè)方法中均沒有一句與異常處理相關(guān)的代碼。

         1: public partial class Default : PageBase
         2: {
         3:     protected void Page_Load(object sender, EventArgs e)
         4:     {
         5:         if (!this.IsPostBack)
         6:         {
         7:             string op1 = Request.QueryString["op1"];
         8:             string op2 = Request.QueryString["op2"];
         9:             if (!string.IsNullOrEmpty(op1) && !string.IsNullOrEmpty(op2))
        10:             {
        11:                 this.txtResult.Text = (int.Parse(op1) / int.Parse(op2)).ToString();
        12:             }
        13:         }
        14:     }
        15:  
        16:     protected void btnCal_Click(object sender, EventArgs e)
        17:     {
        18:         int op1 = int.Parse(this.txtOp1.Text);
        19:         int op2 = int.Parse(this.txtOp2.Text);
        20:         this.txtResult.Text = (op1 / op2).ToString();
        21:     }
        22: }

      現(xiàn)在運(yùn)行我們程序,可以想象如果在表示操作數(shù)的文本框中輸入一個(gè)非整數(shù)字符,調(diào)用Int32的Parse方法時(shí)將會(huì)拋出一個(gè)FormatException異常,或者將被除數(shù)設(shè)置為0,則會(huì)拋出一個(gè)DivideByZeroException異常。如下面的代碼片斷所示,在這兩種情況下相應(yīng)的錯(cuò)誤信息按照我們預(yù)定義的格式以Alert的形式顯示出來。

      三、通過重寫Page的OnLoad和RaisePostBackEvent方法實(shí)現(xiàn)自動(dòng)異常處理

      我們知道ASP.NET應(yīng)用中某個(gè)頁面的后臺(tái)代碼基本上都是注冊(cè)到頁面及其控件的事件處理方法,除了第一次呈現(xiàn)頁面的Load事件,其他事件均是通過PostBack的方式出發(fā)的。所以我最初的解決方案很直接:就是提供一個(gè)PageBase,在重寫的OnLoad和RaisePostBackEvent方法中進(jìn)行異常處理。PageBase的整個(gè)定義如下所示:

         1: public abstract class PageBase: Page
         2: {
         3:     public virtual string ExceptionPolicyName { get; set; }
         4:     public PageBase()
         5:     {
         6:         this.ExceptionPolicyName = "default";
         7:     }
         8:  
         9:     protected virtual string GetExceptionPolicyName()
        10:     {
        11:         ExceptionPolicyAttribute attribute = this.GetType().GetCustomAttributes(true)
        12:             .OfType().FirstOrDefault();
        13:         if (null != attribute)
        14:         {
        15:             return attribute.ExceptionPolicyName;
        16:         }
        17:         else
        18:         {
        19:             return this.ExceptionPolicyName;
        20:         }
        21:     }
        22:  
        23:     protected override void OnLoad(EventArgs e)
        24:     {
        25:         this.InvokeAndHandleException(() => base.OnLoad(e));
        26:     }
        27:  
        28:     protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
        29:     {
        30:         this.InvokeAndHandleException(()=>base.RaisePostBackEvent(sourceControl, eventArgument));
        31:     }
        32:  
        33:     private void InvokeAndHandleException(Action action)
        34:     {
        35:         try
        36:         {
        37:             action();
        38:         }
        39:         catch (Exception ex)
        40:         {
        41:             string exceptionPolicyName = this.GetExceptionPolicyName();
        42:             if (ExceptionPolicy.HandleException(ex, exceptionPolicyName))
        43:             {
        44:                 throw;
        45:             }
        46:         }
        47:     }
        48: }

      如上面的代碼片斷所示,在重寫的OnLoad和RaisePostBackEvent方法中,我們采用與EntLib異常處理應(yīng)用塊的編程方式調(diào)用基類的同名方法。我們通過屬性ExceptionPolicyName 指定了一個(gè)默認(rèn)的異常處理策略名稱(“default”,也正是配置文件中定義個(gè)策略名稱)。如果某個(gè)頁面需要采用其他的異常處理策略,可以在類型上面應(yīng)用ExceptionPolicyAttribute特性來制定,該特性定義如下:

         1: [AttributeUsage( AttributeTargets.Class, AllowMultiple = false)]
         2: public class ExceptionPolicyAttribute: Attribute
         3: {
         4:     public string ExceptionPolicyName { get; private set; }
         5:     public ExceptionPolicyAttribute(string exceptionPolicyName)
         6:     {
         7:         Guard.ArgumentNotNullOrEmpty(exceptionPolicyName, "exceptionPolicyName");
         8:         this.ExceptionPolicyName = exceptionPolicyName;
         9:     }
        10: }


      四、IPostBackDataHandler

      通過為具體Page定義基類并重寫OnLoad和RaisePostBackEvent方法的方式貌似能夠?qū)崿F(xiàn)我們“自動(dòng)化異常處理”的目標(biāo),而且針對(duì)我們提供的這個(gè)實(shí)例來說也是OK的。但是這卻不是正確的解決方案,原因在于并非所有控件的事件都是在RaisePostBackEvent方法執(zhí)行過程中觸發(fā)的。ASP.NET提供了一組實(shí)現(xiàn)了IPostBackDataHandler接口的控件類型,它們會(huì)向PostBack的時(shí)候向服務(wù)端傳遞相應(yīng)的數(shù)據(jù),我們熟悉的ListControl(DropDownList、ListBox、RadioButtonList和CheckBoxList等)就屬于此類。

         1: public interface IPostBackDataHandler
         2: {
         3:     bool LoadPostData(string postDataKey, NameValueCollection postCollection);
         4:     void RaisePostDataChangedEvent();
         5: }

      當(dāng)Page的ProcessRequest(這是對(duì)IHttpHandler方法的實(shí)現(xiàn))的時(shí)候,會(huì)先于RaisePostBackEvent之前調(diào)用另一個(gè)方法RaiseChangedEvents。在RaisePostBackEvent方法執(zhí)行過程中,如果目標(biāo)類型實(shí)現(xiàn)了IPostBackDataHandler接口,會(huì)調(diào)用它們的RaisePostDataChangedEvent方法。很多表示輸入數(shù)據(jù)改變的事件(比如ListControl的SelectedIndexChanged事件)就是被RaisePostDataChangedEvent方法觸發(fā)的。如果可能,我們可以通過重寫RaiseChangedEvents方法的方式來解決這個(gè)問題,不過很可惜,這個(gè)方法是一個(gè)內(nèi)部方法。

      五、EventHandlerWraper

      要實(shí)現(xiàn)“自動(dòng)化異常處理”的根本手段就是將頁面和控件注冊(cè)的事件處理方法置于一個(gè)try/catch塊中執(zhí)行,并采用EntLib的異常處理應(yīng)用塊的方式對(duì)拋出的異常進(jìn)行處理。如果我們能夠改變頁面和控件注冊(cè)的事件,使注冊(cè)的事件處理器本身就具有異常處理的能力,我們“自動(dòng)化異常處理”的目標(biāo)也能夠?qū)崿F(xiàn)。為此我定義了如下一個(gè)用于封裝EventHandler的EventHandlerWrapper,它將EventHandler的置于一個(gè)try/catch塊中執(zhí)行。對(duì)于EventHandlerWrapper的設(shè)計(jì)思想,在我兩年前寫的《如何編寫沒有Try/Catch的程序》一文中具有詳細(xì)介紹。

         1: public class EventHandlerWrapper
         2: {
         3:     public object Target { get; private set; }
         4:     public MethodInfo Method { get; private set; }
         5:     public EventHandler Hander { get; private set; }
         6:     public string ExceptionPolicyName { get; private set; }
         7:  
         8:     public EventHandlerWrapper(EventHandler eventHandler, string exceptionPolicyName)
         9:     {
        10:         Guard.ArgumentNotNull(eventHandler, "eventHandler");
        11:         Guard.ArgumentNotNullOrEmpty(exceptionPolicyName, "exceptionPolicyName");
        12:  
        13:         this.Target = eventHandler.Target;
        14:         this.Method = eventHandler.Method;
        15:         this.ExceptionPolicyName = exceptionPolicyName;
        16:         this.Hander += Invoke;
        17:     }
        18:     public static implicit operator EventHandler(EventHandlerWrapper eventHandlerWrapper)
        19:     {
        20:         Guard.ArgumentNotNull(eventHandlerWrapper, "eventHandlerWrapper");
        21:         return eventHandlerWrapper.Hander;
        22:     }
        23:     private void Invoke(object sender, EventArgs args)
        24:     {
        25:         try
        26:         {
        27:             this.Method.Invoke(this.Target, new object[] { sender, args });
        28:         }
        29:         catch (TargetInvocationException ex)
        30:         {
        31:             if (ExceptionPolicy.HandleException(ex.InnerException, this.ExceptionPolicyName))
        32:             {
        33:                 throw;
        34:             }
        35:         }
        36:     }
        37: }

      由于我們?yōu)镋ventHandlerWrapper定義了一個(gè)針對(duì)EventHandler的隱式轉(zhuǎn)化符,一個(gè)EventHandlerWrapper對(duì)象能夠自動(dòng)被轉(zhuǎn)化成EventHandler對(duì)象。我們現(xiàn)在的目標(biāo)就是:將包括頁面在內(nèi)的所有控件注冊(cè)的EventHandler替換成用于封裝它們的EventHandlerWrapper。我們知道所有控件的基類Control具有如下一個(gè)受保護(hù)的只讀屬性Events,所有注冊(cè)的EventHandler就包含在這里,而我們的目標(biāo)就是要改變所有控件該屬性中保存的EventHandler。

         1: public class Control
         2: {
         3:     protected EventHandlerList Events{get;}
         4: }

      其實(shí)要改變Events屬性中的EventHandler也并不是一件容易的事,因?yàn)槠漕愋虴ventHandlerList 并不如它的名稱表現(xiàn)出來的那樣是一個(gè)可枚舉的列表,而是一個(gè)通過私有類型ListEntry維護(hù)的鏈表。要改變這些注冊(cè)的事件,我們不得不采用反射,而這會(huì)影響性能。不過對(duì)應(yīng)并非訪問量不高的企業(yè)應(yīng)用來說,我覺得這點(diǎn)性能損失是可以接受的。整個(gè)操作被定義在如下所示的EventHandlerWrapperUtil的Wrap方法中。

         1: private static class EventHandlerWrapperUtil
         2: {
         3:     private static Type listEntryType;
         4:     private static FieldInfo handler;
         5:     private static FieldInfo key;
         6:     private static FieldInfo next;
         7:  
         8:     static EventHandlerWrapperUtil()
         9:     {
        10:         listEntryType = Type.GetType("System.ComponentModel.EventHandlerList+ListEntry, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
        11:         BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
        12:         handler = listEntryType.GetField("handler", bindingFlags);
        13:         key     = listEntryType.GetField("key", bindingFlags);
        14:         next    = listEntryType.GetField("next", bindingFlags);
        15:     }
        16:  
        17:     public static void Wrap(object listEntry, string exceptionPolicyName)
        18:     {
        19:         EventHandler eventHandler = handler.GetValue(listEntry) as EventHandler;
        20:         if (null != eventHandler)
        21:         {
        22:             EventHandlerWrapper eventHandlerWrapper = new EventHandlerWrapper(eventHandler, exceptionPolicyName);
        23:             handler.SetValue(listEntry, (EventHandler)eventHandlerWrapper);
        24:         }
        25:             object nextEntry = next.GetValue(listEntry);
        26:         if(null != nextEntry)
        27:         {
        28:             Wrap(nextEntry,exceptionPolicyName);
        29:         }
        30:     }            
        31: }


      六、對(duì)控件注冊(cè)事件的自動(dòng)封裝

      對(duì)包括頁面在內(nèi)的所有控件注冊(cè)時(shí)間的自動(dòng)封裝同樣實(shí)現(xiàn)在作為具體頁面積累的PageBase中。具體的實(shí)現(xiàn)定義在WrapEventHandlers方法中,由于Control的Events屬性是受保護(hù)的,所以我們還得采用反射。該方法最終的重寫的OnInit方法中執(zhí)行。

         1: public abstract class PageBase : Page
         2: {
         3:     private static PropertyInfo eventsProperty;
         4:     private static FieldInfo headField;
         5:  
         6:     public static string ExceptionPolicyName { get; set; }
         7:     static PageBase()
         8:     {
         9:         ExceptionPolicyName = "default";
        10:         eventsProperty = typeof(Control).GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
        11:         headField = typeof(EventHandlerList).GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
        12:     }
        13:  
        14:     protected override void OnInit(EventArgs e)
        15:     {
        16:         base.OnInit(e);
        17:         Trace.Write("Begin to wrap events!");
        18:         this.WrapEventHandlers(this);
        19:         Trace.Write("Wrapping events ends!");
        20:     }
        21:  
        22:     protected virtual void WrapEventHandlers(Control control)
        23:     {
        24:         string exceptionPolicyName = this.GetExceptionPolicyName();
        25:         EventHandlerList events = eventsProperty.GetValue(control, null) as EventHandlerList;
        26:         if (null != events)
        27:         {
        28:             object head = headField.GetValue(events);
        29:             if (null != head)
        30:             {
        31:                 EventHandlerWrapperUtil.Wrap(head, exceptionPolicyName);
        32:             }
        33:         }
        34:         foreach (Control subControl in control.Controls)
        35:         {
        36:             WrapEventHandlers(subControl);
        37:         }
        38:     }
        39:  
        40:     protected virtual string GetExceptionPolicyName()
        41:     {
        42:         ExceptionPolicyAttribute attribute = this.GetType().GetCustomAttributes(true)
        43:             .OfType().FirstOrDefault();
        44:         if (null != attribute)
        45:         {
        46:             return attribute.ExceptionPolicyName;
        47:         }
        48:         else
        49:         {
        50:             return ExceptionPolicyName;
        51:         }
        52:     }
        53: }


      七、AlertHandler

      我想有人對(duì)用于顯示錯(cuò)誤消息對(duì)話框的AltertHandler的實(shí)現(xiàn)很感興趣,下面給出了它和對(duì)應(yīng)的AlertHandlerData的定義。從如下的代碼可以看出,AltertHandler僅僅是調(diào)用Page的RaisePostBackEvent方法注冊(cè)了一段顯示錯(cuò)誤消息的JavaScript腳本而已。

         1: [ConfigurationElementType(typeof(AlertHandlerData))]
         2: public class AlertHandler: IExceptionHandler
         3: {
         4:     public string MessageTemplate { get; private set; }
         5:     public AlertHandler(string messageTemplate)
         6:     {
         7:         this.MessageTemplate = messageTemplate;
         8:     }
         9:  
        10:     protected string FormatMessage(Exception exception)
        11:     {
        12:         Guard.ArgumentNotNull(exception, "exception");
        13:         string messageTemplate = string.IsNullOrEmpty(this.MessageTemplate) ? exception.Message : this.MessageTemplate;
        14:         return messageTemplate.Replace("{ExceptionType}", exception.GetType().Name)
        15:                                 .Replace("{HelpLink}", exception.HelpLink)
        16:                                 .Replace("{Message}", exception.Message)
        17:                                 .Replace("{Source}", exception.Source)
        18:                                 .Replace("{StackTrace}", exception.StackTrace);
        19:     }
        20:  
        21:     public Exception HandleException(Exception exception, Guid handlingInstanceId)
        22:     {
        23:         Page page = HttpContext.Current.Handler as Page;
        24:         if (null != page)
        25:         {
        26:  
        27:             string message = this.FormatMessage(exception);
        28:             string hiddenControl = "hiddenCurrentPageException";
        29:             page.ClientScript.RegisterHiddenField(hiddenControl, message);
        30:             string script = string.Format("", 
        31:                 new object[] { hiddenControl });
        32:             page.ClientScript.RegisterStartupScript(base.GetType(), "ExceptionHandling.AlertHandler", script);
        33:         }
        34:         return exception;
        35:     }
        36: }
        37:  
        38: public class AlertHandlerData : ExceptionHandlerData
        39: {
        40:     [ConfigurationProperty("messageTemplate", IsRequired = false, DefaultValue="")]
        41:     public string MessageTemplate
        42:     {
        43:         get { return (string)this["messageTemplate"]; }
        44:         set { this["messageTemplate"] = value; }
        45:     }
        46:  
        47:     public override IEnumerable GetRegistrations(string namePrefix)
        48:     {
        49:         yield return new TypeRegistration(() => new AlertHandler(this.MessageTemplate))
        50:         {
        51:             Name = this.BuildName(namePrefix),
        52:             Lifetime = TypeRegistrationLifetime.Transient
        53:         };
        54:     }
        55: }

        相關(guān)評(píng)論

        閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評(píng)論

        最新評(píng)論

        發(fā)表評(píng)論 查看所有評(píng)論(0)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過審核才能顯示)