System.Transactionsは、同じデータベースへの複数の接続を含むトランザクションをDTCにエスカレートすることで有名です。以下のモジュールとヘルパークラスはConnectionContext
、同じデータベースに対する複数の接続要求が同じ接続オブジェクトを返すようにすることで、これを防ぐことを目的としています。これは、ある意味でメモ化ですが、メモ化されるものは複数あり、2番目は最初のものに依存しています。このモジュールで同期および/または可変状態(おそらくメモ化を使用)を非表示にする方法、またはより機能的なスタイルで書き直す方法はありますか?
(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)