1 people like it.

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
Raw view Test code New version

More information

Link:http://fssnip.net/lg
Posted:10 years ago
Author:Eirik Tsarpalis
Tags: exception , stacktraces