.NET:如何應對邊界異常?


背景

為什么語言引入了異常

一直沒有思考過這個問題,但是異常確實讓我的編程生活更快樂,今天早上似乎找到了這個問題的答案:exception之於call stack就像break和continue之於while或for、就像return之於method,總結為一句話:異常只是一種返回機制。

為什么異常讓程序更簡潔

代碼里只有正常的處理邏輯。

 1         /// <summary>
 2         /// 創建。
 3         /// </summary>
 4         public ActionResult Create(TAggregateRoot item)
 5         {
 6             this.CurrentCommandService.Execute(new TCreateCommand
 7             {
 8                 Aggregate = item
 9             });
10 
11             return this.NewtonsoftJson(new
12             {
13                 success = true,
14                 items = this.GetById(item.Id)
15             });
16         }
17 
18         /// <summary>
19         /// 修改。
20         /// </summary>
21         public ActionResult Update(TAggregateRoot item)
22         {
23             this.CurrentCommandService.Execute(new TUpdateCommand
24             {
25                 Aggregate = item
26             });
27 
28             return this.NewtonsoftJson(new
29             {
30                 success = true,
31                 items = this.GetById(item.Id)
32             });
33         }


我的程序代碼基本上也是CQRS的,凡是以寫為目的的,都是用void進行聲明。

異常的處理

異常有五種處理思路,如下圖所示:

關於這五種處理思路的概要介紹可以參考這篇文章:http://www.cnblogs.com/happyframework/archive/2013/04/09/3010082.html

今天的主要目的是介紹“邊界異常處理”。

邊界異常處理

當異常到達邊界,毫無疑問我們必須進行處理。總體來說,到達邊界的異常分為兩大類:我們有意拋出的異常和未處理異常,針對這兩種異常我們需要不同的處理思路,如:

  1. 有意拋出的異常:不希望寫入日志,希望顯示到UI。
  2. 未處理的異常:希望寫入日志,不希望直接顯示到UI,希望定制這種異常的顯示信息。

一個簡單的邊界異常處理框架

結構

下圖的友好異常就是我們有意拋出的異常或我們能意識到的異常。

幾個核心類型

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Web.Mvc;
 7 
 8 namespace Happy.Web.Mvc.ExceptionHanding
 9 {
10     /// <summary>
11     /// 異常信息提供者接口,如果你希望為異常返回更多的信息,可以實現該接口。
12     /// </summary>
13     public interface IExceptionInformationProvider
14     {
15         /// <summary>
16         /// 創建信息。
17         /// </summary>
18         object CreateInformation(Exception exception);
19     }
20 }
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Web.Mvc;
 7 
 8 using Common.Logging;
 9 using Happy.Web.Mvc.Newtonsoft;
10 
11 namespace Happy.Web.Mvc.ExceptionHanding
12 {
13     /// <summary>
14     /// 處理應用程序未捕獲的異常。
15     /// </summary>
16     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
17     public class WriteExceptionResultAttribute : FilterAttribute, IExceptionFilter
18     {
19         /// <inheritdoc />
20         public void OnException(ExceptionContext filterContext)
21         {
22             var exception = filterContext.Exception;
23 
24             this.LogException(exception);
25 
26             filterContext.Result = ExceptionInformationProviderRegistry.CreateErrorResult(exception);
27 
28             filterContext.ExceptionHandled = true;
29         }
30 
31         private void LogException(Exception exception)
32         {
33             if (FriendlyExceptionRegistry.Contains(exception.GetType()))
34             {
35                 return;
36             }
37 
38             LogManager.GetCurrentClassLogger().Error(exception);
39         }
40     }
41 }
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Web.Mvc;
 7 using System.Data;
 8 
 9 using Happy.ExtensionMethod;
10 using Happy.DesignByContract;
11 using Happy.Web.Mvc.Newtonsoft;
12 
13 namespace Happy.Web.Mvc.ExceptionHanding
14 {
15     /// <summary>
16     /// 異常信息提供者注冊處。
17     /// </summary>
18     public static class ExceptionInformationProviderRegistry
19     {
20         private static readonly Dictionary<Type, IExceptionInformationProvider> _providers
21             = new Dictionary<Type, IExceptionInformationProvider>();
22 
23         static ExceptionInformationProviderRegistry()
24         {
25             Register<OptimisticConcurrencyException>(new Internal.OptimisticConcurrencyExceptionInformationProvider());
26         }
27 
28         /// <summary>
29         /// 注冊提供者。
30         /// </summary>
31         public static void Register<TException>(IExceptionInformationProvider provider)
32             where TException : Exception
33         {
34             Register(typeof(TException), provider);
35         }
36 
37         /// <summary>
38         /// 注冊提供者。
39         /// </summary>
40         public static void Register(Type exceptionType, IExceptionInformationProvider provider)
41         {
42             exceptionType.MustNotNull("exceptionType");
43             provider.MustNotNull("provider");
44 
45             _providers[exceptionType] = provider;
46         }
47 
48         internal static ActionResult CreateErrorResult(Exception exception)
49         {
50             exception.MustNotNull("exception");
51 
52             var exceptionType = exception.GetType();
53 
54             var information = CreateDefaultInformation(exception);
55             
56             if (_providers.ContainsKey(exceptionType))
57             {
58                 var extInformation = _providers[exceptionType].CreateInformation(exception);
59 
60                 foreach (var item in extInformation.ToDictionary())
61                 {
62                     information[item.Key] = item.Value;
63                 }
64             }
65 
66             return new NewtonsoftJsonResult
67             {
68                 Data = information
69             };
70         }
71 
72         private static Dictionary<string, object> CreateDefaultInformation(Exception exception)
73         {
74             return new Dictionary<string, object> 
75             { 
76                 { "success", false },
77                 { "exception", exception.GetType().Name },
78                 { "message",exception.Message }
79             };
80         }
81     }
82 }

為樂觀並發異常自定義返回消息

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Data;
 7 using System.Web.Mvc;
 8 
 9 using Happy.Web.Mvc.Newtonsoft;
10 
11 namespace Happy.Web.Mvc.ExceptionHanding.Internal
12 {
13     internal sealed class OptimisticConcurrencyExceptionInformationProvider : ExceptionInformationProvider<OptimisticConcurrencyException>
14     {
15         protected override object CreateResult(OptimisticConcurrencyException exception)
16         {
17             return new
18             {
19                 message = Messages.Error_OptimisticConcurrencyExceptionMessage
20             };
21         }
22     }
23 }

備注

像微軟的異常處理框架都是一個非常好的東西。

合理的利用和使用異常會讓程序的結構更加簡潔,這些概念只有真正使用了才能深刻的明白。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM