Created
May 20, 2016 10:46
-
-
Save Thorium/5494777d9e85ce172aba7a907ecdab4a to your computer and use it in GitHub Desktop.
Way to wrap methods to transactions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1) Create TransactionScope, on Mono and in .NET, and the later one supports TransactionScopeAsyncFlowOption. | |
// 2) Then, complete the transaction automatically if no exceptions has been thrown. | |
// If Async or Task, then automatically await the result before complete without blocking the thread. | |
#if INTERACTIVE | |
#r "System.Transactions.dll" | |
#endif | |
open System | |
open System.Threading | |
open System.Threading.Tasks | |
open System.Transactions | |
let logmsg act threadid transId = | |
let tidm = match String.IsNullOrEmpty threadid with | true -> "" | false -> " at thread " + threadid | |
let msg = "Transaction " + transId + " " + act + tidm | |
Console.WriteLine msg | |
//Logary.Logger.log (Logary.Logging.getCurrentLogger()) (Logary.Message.eventDebug msg) |> start | |
let getTransactionId() = | |
match Transaction.Current <> null && Transaction.Current.TransactionInformation <> null with | |
| true -> Transaction.Current.TransactionInformation.LocalIdentifier | |
| false -> "" | |
let transactionWithManualComplete<'T>() = | |
match Type.GetType ("Mono.Runtime") <> null with | |
| true -> new Transactions.TransactionScope() | |
| false -> | |
// Mono would fail to compilation, so we have to construct this via reflection: | |
// new Transactions.TransactionScope(Transactions.TransactionScopeAsyncFlowOption.Enabled) | |
let transactionAssembly = System.Reflection.Assembly.GetAssembly typeof<TransactionScope> | |
let asynctype = transactionAssembly.GetType "System.Transactions.TransactionScopeAsyncFlowOption" | |
let transaction = typeof<TransactionScope>.GetConstructor [|asynctype|] | |
transaction.Invoke [|1|] :?> TransactionScope | |
let transactionWith<'T> (func: unit -> 'T) = | |
use scope = transactionWithManualComplete() | |
let transId = getTransactionId() | |
logmsg "started" Thread.CurrentThread.Name transId | |
let res = func() | |
match box res with | |
| :? Task as task -> | |
let commit = Action<Task>(fun a -> | |
logmsg "completed" Thread.CurrentThread.Name transId | |
scope.Complete() | |
) | |
let commitTran1 = task.ContinueWith(commit, TaskContinuationOptions.OnlyOnRanToCompletion) | |
let commitTran2 = task.ContinueWith((fun _ -> | |
logmsg "failed" Thread.CurrentThread.Name transId), TaskContinuationOptions.NotOnRanToCompletion) | |
res | |
| item when item <> null && item.GetType().Name = "FSharpAsync`1" -> | |
let msg = "Use transactionWithAsync" | |
//Logary.Logger.log (Logary.Logging.getCurrentLogger()) (Logary.Message.eventError msg) |> start | |
failwith msg | |
| _ -> | |
logmsg "completed" System.Threading.Thread.CurrentThread.Name transId | |
scope.Complete() | |
res | |
let transactionWithAsync<'T> (func: unit -> Async<'T>) = | |
async { | |
use scope = transactionWithManualComplete() | |
let transId = getTransactionId() | |
logmsg "started" Thread.CurrentThread.Name transId | |
let! res = func() | |
logmsg "completed" Thread.CurrentThread.Name transId | |
scope.Complete() | |
return res | |
} | |
(* | |
let ``example usage`` = | |
transactionWith <| fun () -> | |
Console.WriteLine "Normal source code in transaction" | |
Console.WriteLine "e.g. database operations!" | |
Console.WriteLine "Return values and " | |
Console.WriteLine "C# Task classes supported." | |
let ``example usage FSharpAsync`` = | |
transactionWithAsync <| fun () -> async { | |
Console.WriteLine "Async source code in transaction" | |
return "hello!" | |
} | |
exampleusageFSharpAsync |> Async.RunSynchronously;; | |
*) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment