Да, это похоже на запоминание - запоминание всегда должно быть реализовано с использованием мутации в F #, поэтому тот факт, что вы используете изменяемые коллекции, в принципе не является проблемой.
Я думаю, вы могли бы попытаться упростить это, ища повторяющиеся шаблоны в коде. Если я понимаю, ваш код фактически реализует двухуровневый кеш, если первый ключ - это идентификатор транзакции, а второй - строка подключения. Вы можете попытаться упростить его, создав тип, который реализует одноуровневое кэширование, а затем составьте свой менеджер транзакций, вложив кэш два раза.
Я не пытался повторно реализовать его во всех деталях, но одноуровневый кеш может выглядеть так:
// Takes a function that calculates a value for a given 'Key
// when it is not available (the function also gets a flag indicating
// whether it is the first one, so that you can register it with transaction0
type Cache<´Key, ´Value when ´Key : equality>(createFunc) =
let dict = new Dictionary<´Key, ´Value>()
// Utility function that implements global lock for the object
let locked =
let locker = new obj()
(fun f -> lock locker f)
member x.Remove(key) =
locked (fun () -> dict.Remove(key))
// Get item from the cache using the cache.Item[key] syntax
member x.Item
with get(key) =
match dict.TryGetValue(key) with
| true, res -> res
| false, _ ->
// Calculate the value if it is not already available
let res = createFunc (dict.Count = 0) key
locked (fun () -> dict.Add(key, res))
res
Теперь я думаю, что ваш TransactionManager
может быть реализован с использованием типа:
Cache<string, Cache<string, Connection>>
Было бы неплохо использовать принцип композиционности, который необходим для функционального программирования. Я предполагаю, что вам может понадобиться сделать тип Cache
немного более сложным (чтобы он вызывал функцию, указанную вами в различных других ситуациях, например, при удалении значения), но в принципе вы могли бы начать с попытки реализовать свой менеджер используя вышеуказанный класс.