当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#的东西,有没有办法在设置值时捕获这个异常?
答案 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