众所周知,System.Transactions 将涉及到同一数据库的多个连接的事务升级到 DTC。下面的模块和帮助程序类ConnectionContext
旨在通过确保对同一数据库的多个连接请求返回相同的连接对象来防止这种情况。从某种意义上说,这就是记忆,尽管有很多东西被记忆,第二个依赖于第一个。有没有办法在这个模块中隐藏同步和/或可变状态(可能使用记忆),或者可能以更实用的风格重写它?
(通过连接字符串获取连接时没有锁定可能一文不值,因为 Transaction.Current 是ThreadStatic
。)
type ConnectionContext(connection:IDbConnection, ownsConnection) =
member x.Connection = connection
member x.OwnsConnection = ownsConnection
interface IDisposable with
member x.Dispose() = if ownsConnection then connection.Dispose()
module ConnectionManager =
let private _connections = new Dictionary<string, Dictionary<string, IDbConnection>>()
let private getTid (t:Transaction) = t.TransactionInformation.LocalIdentifier
let private removeConnection tid =
let cl = _connections.[tid]
for (KeyValue(_, con)) in cl do
con.Close()
lock _connections (fun () -> _connections.Remove(tid) |> ignore)
let getConnection connectionString (openConnection:(unit -> IDbConnection)) =
match Transaction.Current with
| null -> new ConnectionContext(openConnection(), true)
| current ->
let tid = getTid current
// get connections for the current transaction
let connections =
match _connections.TryGetValue(tid) with
| true, cl -> cl
| false, _ ->
let cl = Dictionary<_,_>()
lock _connections (fun () -> _connections.Add(tid, cl))
cl
// find connection for this connection string
let connection =
match connections.TryGetValue(connectionString) with
| true, con -> con
| false, _ ->
let initial = (connections.Count = 0)
let con = openConnection()
connections.Add(connectionString, con)
// if this is the first connection for this transaction, register connections for cleanup
if initial then
current.TransactionCompleted.Add
(fun args ->
let id = getTid args.Transaction
removeConnection id)
con
new ConnectionContext(connection, false)