System.Transactions escalade notoirement les transactions impliquant plusieurs connexions à la même base de données vers le DTC. Le module et la classe d'assistance, ConnectionContext
, ci-dessous sont destinés à empêcher cela en assurant que plusieurs demandes de connexion pour la même base de données retournent le même objet de connexion. C'est, dans un certain sens, mémo, bien qu'il y ait plusieurs choses mémoized et la seconde dépend de la première. Est-il possible de masquer la synchronisation et/ou l'état mutable (peut-être en utilisant la mémorisation) dans ce module, ou peut-être de le réécrire dans un style plus fonctionnel?Est-ce que quelque chose dans le genre de la mémoisation imbriquée est nécessaire ici?
(Il est peut-être ne vaut rien qu'il n'y a pas de blocage lors de l'obtention de la connexion par chaîne de connexion, car Transaction.Current est 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)
Cela ressemble à un très bon point de départ. J'ai souvent découvert, lors de l'utilisation de l'état mutable, que je mettais en œuvre un motif déjà présent dans le cadre (p., paresseux, Seq.fold). Donc, je voulais voir s'il y avait un motif dans ce code que je néglige. – Daniel
Je ne pense pas que cela soit implémenté n'importe où dans la bibliothèque, mais je pense que quelque chose de très similaire à mon code apparaît dans Expert F #. Un exemple qui montre la mémorisation dans ma programmation fonctionnelle du monde réel implémente le motif simplement comme une fonction qui prend une fonction (qui ne permet pas de supprimer des valeurs mémorisées). Je suppose que la mémorisation nécessite un réglage (par exemple, comment vous stockez les valeurs, comment vous les supprimez, ...), donc il n'y a pas de classe intégrée pour cela (car il peut ne pas répondre aux besoins dans de nombreux cas). –