1 people like it.
Like the snippet!
Pluggable Exception Contexts
.Net exceptions carry with them lots of useful metadata that are not directly user modifiable. In certain applications, such as distributed or symbolic execution it might be reasonable to need to modify such metadata. The following pattern is a proposal on effectively decoupling an exception from its contextual metadata, as well as a way of defining exception hierarchies while avoiding all the ISerializable boilerplate.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
|
open System
open System.Collections
open System.Runtime.Serialization
type ExceptionContext =
{
Data : IDictionary
HelpUrl : string
StackTrace : string
RemoteStackTrace : string
RemoteStackIndex : int
ExceptionMethod : string
HResult : int
Source : string
WatsonBuckets : byte []
}
with
static member OfException(e : exn) =
let sI = new SerializationInfo(typeof<exn>, FormatterConverter())
let sc = new StreamingContext()
e.GetObjectData(sI,sc)
{
Data = sI.GetValue("Data", typeof<IDictionary>) :?> IDictionary
HelpUrl = sI.GetString("HelpURL")
StackTrace = sI.GetString("StackTraceString")
RemoteStackTrace = sI.GetString("RemoteStackTraceString")
RemoteStackIndex = sI.GetInt32("RemoteStackIndex")
ExceptionMethod = sI.GetString("ExceptionMethod")
HResult = sI.GetInt32("HResult")
Source = sI.GetString("Source")
WatsonBuckets = sI.GetValue("WatsonBuckets", typeof<byte []>) :?> byte []
}
member internal c.WriteContextData(sI : SerializationInfo) =
sI.AddValue("Data", c.Data, typeof<IDictionary>)
sI.AddValue("HelpURL", c.HelpUrl)
sI.AddValue("StackTraceString", c.StackTrace)
sI.AddValue("RemoteStackTraceString", c.RemoteStackTrace)
sI.AddValue("RemoteStackIndex", c.RemoteStackIndex)
sI.AddValue("ExceptionMethod", c.ExceptionMethod)
sI.AddValue("HResult", c.HResult)
sI.AddValue("Source", c.Source)
sI.AddValue("WatsonBuckets", c.WatsonBuckets, typeof<byte []>)
type ContextualException =
inherit Exception
new(message : string, ?inner : exn, ?context : ExceptionContext) =
match inner, context with
| None, None -> { inherit Exception(message) }
| Some e, None -> { inherit Exception(message, e) }
| _, Some context ->
let sI = new SerializationInfo(typeof<exn>, FormatterConverter())
let sc = new StreamingContext()
sI.AddValue("ClassName", "System.Exception")
sI.AddValue("Message", message)
sI.AddValue("InnerException", defaultArg inner null, typeof<Exception>)
context.WriteContextData(sI)
{ inherit Exception(sI, sc) }
member e.GetExceptionContext() = ExceptionContext.OfException e
// example
type Test(msg : string, ?inner, ?ctx) =
inherit ContextualException(msg, ?inner = inner, ?context = ctx)
// throws a deep exception
let rec dig n =
if n = 0 then raise <| Test("foo")
else
1 + dig (n-1)
// catch
let e = try dig 40 |> ignore ; failwith "" with :? Test as e -> e
// evaluate
let ctx = e.GetExceptionContext()
// patch
let e' = Test("bar", inner = System.Exception("nested"), ctx = ctx)
// verify
e'.ToString()
|
namespace System
namespace System.Collections
namespace System.Runtime
namespace System.Runtime.Serialization
type ExceptionContext =
{Data: IDictionary;
HelpUrl: string;
StackTrace: string;
RemoteStackTrace: string;
RemoteStackIndex: int;
ExceptionMethod: string;
HResult: int;
Source: string;
WatsonBuckets: byte [];}
member internal WriteContextData : sI:SerializationInfo -> unit
static member OfException : e:exn -> ExceptionContext
Full name: Script.ExceptionContext
Multiple items
ExceptionContext.Data: IDictionary
--------------------
namespace System.Data
--------------------
namespace Microsoft.FSharp.Data
type IDictionary =
member Add : key:obj * value:obj -> unit
member Clear : unit -> unit
member Contains : key:obj -> bool
member GetEnumerator : unit -> IDictionaryEnumerator
member IsFixedSize : bool
member IsReadOnly : bool
member Item : obj -> obj with get, set
member Keys : ICollection
member Remove : key:obj -> unit
member Values : ICollection
Full name: System.Collections.IDictionary
ExceptionContext.HelpUrl: string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
ExceptionContext.StackTrace: string
ExceptionContext.RemoteStackTrace: string
ExceptionContext.RemoteStackIndex: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
ExceptionContext.ExceptionMethod: string
ExceptionContext.HResult: int
ExceptionContext.Source: string
ExceptionContext.WatsonBuckets: byte []
Multiple items
val byte : value:'T -> byte (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.byte
--------------------
type byte = Byte
Full name: Microsoft.FSharp.Core.byte
static member ExceptionContext.OfException : e:exn -> ExceptionContext
Full name: Script.ExceptionContext.OfException
val e : exn
type exn = Exception
Full name: Microsoft.FSharp.Core.exn
val sI : SerializationInfo
Multiple items
type SerializationInfo =
new : type:Type * converter:IFormatterConverter -> SerializationInfo
member AddValue : name:string * value:obj -> unit + 15 overloads
member AssemblyName : string with get, set
member FullTypeName : string with get, set
member GetBoolean : name:string -> bool
member GetByte : name:string -> byte
member GetChar : name:string -> char
member GetDateTime : name:string -> DateTime
member GetDecimal : name:string -> decimal
member GetDouble : name:string -> float
...
Full name: System.Runtime.Serialization.SerializationInfo
--------------------
SerializationInfo(type: Type, converter: IFormatterConverter) : unit
val typeof<'T> : Type
Full name: Microsoft.FSharp.Core.Operators.typeof
Multiple items
type FormatterConverter =
new : unit -> FormatterConverter
member Convert : value:obj * type:Type -> obj + 1 overload
member ToBoolean : value:obj -> bool
member ToByte : value:obj -> byte
member ToChar : value:obj -> char
member ToDateTime : value:obj -> DateTime
member ToDecimal : value:obj -> decimal
member ToDouble : value:obj -> float
member ToInt16 : value:obj -> int16
member ToInt32 : value:obj -> int
...
Full name: System.Runtime.Serialization.FormatterConverter
--------------------
FormatterConverter() : unit
val sc : StreamingContext
Multiple items
type StreamingContext =
struct
new : state:StreamingContextStates -> StreamingContext + 1 overload
member Context : obj
member Equals : obj:obj -> bool
member GetHashCode : unit -> int
member State : StreamingContextStates
end
Full name: System.Runtime.Serialization.StreamingContext
--------------------
StreamingContext()
StreamingContext(state: StreamingContextStates) : unit
StreamingContext(state: StreamingContextStates, additional: obj) : unit
Exception.GetObjectData(info: SerializationInfo, context: StreamingContext) : unit
Multiple items
namespace System.Data
--------------------
namespace Microsoft.FSharp.Data
SerializationInfo.GetValue(name: string, type: Type) : obj
SerializationInfo.GetString(name: string) : string
SerializationInfo.GetInt32(name: string) : int
val c : ExceptionContext
member internal ExceptionContext.WriteContextData : sI:SerializationInfo -> unit
Full name: Script.ExceptionContext.WriteContextData
SerializationInfo.AddValue(name: string, value: DateTime) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: decimal) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: float) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: float32) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: uint64) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: int64) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: uint32) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: int) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: uint16) : unit
(+0 other overloads)
SerializationInfo.AddValue(name: string, value: int16) : unit
(+0 other overloads)
ExceptionContext.Data: IDictionary
Multiple items
type ContextualException =
inherit Exception
new : message:string * ?inner:exn * ?context:ExceptionContext -> ContextualException
member GetExceptionContext : unit -> ExceptionContext
Full name: Script.ContextualException
--------------------
new : message:string * ?inner:exn * ?context:ExceptionContext -> ContextualException
Multiple items
type Exception =
new : unit -> Exception + 2 overloads
member Data : IDictionary
member GetBaseException : unit -> Exception
member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
member GetType : unit -> Type
member HelpLink : string with get, set
member InnerException : Exception
member Message : string
member Source : string with get, set
member StackTrace : string
...
Full name: System.Exception
--------------------
Exception() : unit
Exception(message: string) : unit
Exception(message: string, innerException: exn) : unit
Exception(info: SerializationInfo, context: StreamingContext) : unit
val message : string
val inner : exn option
val context : ExceptionContext option
union case Option.None: Option<'T>
union case Option.Some: Value: 'T -> Option<'T>
val context : ExceptionContext
val defaultArg : arg:'T option -> defaultValue:'T -> 'T
Full name: Microsoft.FSharp.Core.Operators.defaultArg
member internal ExceptionContext.WriteContextData : sI:SerializationInfo -> unit
val e : ContextualException
member ContextualException.GetExceptionContext : unit -> ExceptionContext
Full name: Script.ContextualException.GetExceptionContext
static member ExceptionContext.OfException : e:exn -> ExceptionContext
Multiple items
type Test =
inherit ContextualException
new : msg:string * ?inner:exn * ?ctx:ExceptionContext -> Test
Full name: Script.Test
--------------------
new : msg:string * ?inner:exn * ?ctx:ExceptionContext -> Test
val msg : string
val ctx : ExceptionContext option
val dig : n:int -> int
Full name: Script.dig
val n : int
val raise : exn:Exception -> 'T
Full name: Microsoft.FSharp.Core.Operators.raise
val e : Test
Full name: Script.e
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
val failwith : message:string -> 'T
Full name: Microsoft.FSharp.Core.Operators.failwith
val e : Test
val ctx : ExceptionContext
Full name: Script.ctx
member ContextualException.GetExceptionContext : unit -> ExceptionContext
val e' : Test
Full name: Script.e'
Multiple items
type Exception =
new : unit -> Exception + 2 overloads
member Data : IDictionary
member GetBaseException : unit -> Exception
member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
member GetType : unit -> Type
member HelpLink : string with get, set
member InnerException : Exception
member Message : string
member Source : string with get, set
member StackTrace : string
...
Full name: System.Exception
--------------------
Exception() : unit
Exception(message: string) : unit
Exception(message: string, innerException: exn) : unit
Exception.ToString() : string
More information