设置值时是否可以捕获TypeInitializationException?

时间:2015-04-02 12:53:55

标签: linq-to-sql f# type-providers

当app.config文件未提供有效连接时使用SQL类型提供程序时,将引发TypeInitializationException。我想捕获此异常并返回更有用的消息(例如“您的app.config文件丢失”),但我遇到了麻烦。我尝试了以下内容:

open System.Data
open System.Data.Linq
open Microsoft.FSharp.Data.TypeProviders

type SqlConnection = Microsoft.FSharp.Data.TypeProviders.SqlDataConnection<ConnectionStringName = "DBConnectionString">

let DB1 =
    try  SqlConnection.GetDataContext()
    with | ex -> failwith "Exception 1"

当我尝试捕获它时,我捕获的异常实际上是一个NullReferenceException,然后我的“Exception 1”被包装在TypeInitializationException中。

我可以这样做:

let getDB2 () =
    try DB1
    with | ex -> failwith "Exception 2"

...其中ex是TypeInitializationException,然后它按我的意愿抛出异常2,但是当我尝试时:

let DB3 =
    try getDB2()
    with | ex -> failwith "Exception 3"

...然后它被包装在另一个TypeInitializationException中,奇怪的是它包装了Exception 1而不是Exception 3,似乎绕过了DB3中的try-with块。 (我应该注意,当我正在进行测试时,我会用C#调用所有这些东西,以防万一。)

是否可以避免在尝试访问数据库时将泛型TypeInitializationException作为外部异常,而不将其作为函数公开?为什么TypeInitializationException没有像其他异常一样被捕获?

---附录---

在多了一点之后,我发现设置ANY值时会出现这种情况。例如,这个:

let x : int =
    try failwith "Exception A"
    with | ex -> failwith "Exception B"

...导致包含异常B的TypeInitializationException。

这是一个F#的东西,有没有办法在设置值时捕获这个异常?

2 个答案:

答案 0 :(得分:2)

您的DB1不是功能,而是。在加载模块时会对其进行评估,这在.NET中首次尝试从模块访问任何内容时都会发生。这就是为什么一旦你尝试任何事情就会抛出异常的原因。

此外,在.NET中,在类的静态初始化期间抛出的任何异常(以及F#模块被编译为.NET类)都包含在TypeInitializationException中(我不清楚这背后的基本原理)

如果您希望DB1成为函数,并且仅在调用时进行求值,请为其指定参数。如果您没有任何有意义的参数,请将其设为unit(在F#中用一对空括号表示):

let DB1 () =
    try  SqlConnection.GetDataContext()
    with | ex -> failwith "Exception 1"

答案 1 :(得分:0)

您的DB1是一次性资源,因此,您无论如何都不应该在模块初始化期间创建,而是返回它。

let createDB1 () = new SqlConnection.GetDataContext()

并将其丢弃在另一个适当的地方。或者(我个人的偏好,因为你不太可能忘记处理)包裹完全隐藏DB1,而只是将它注入函数

let usingDB1 f = 
    use db = new SqlConnection.GetDataContext()
    f db

let myFunc db = // do something meaningful here

usingDB1 myFunc