我在这里创建了一个最小的复制品:https://github.com/mushishi78/transaction-minimal-reproduction
基本上我已经达到了 mono 的
NotImplementedException
给出的
TransactionInterop
,这似乎意味着它还没有准备好进行异步事务。但由于我对 .NET 没有那么丰富的经验,我希望得到一些第二意见或建议。
我在实现中做了一些奇怪的事情吗?如果我写得正确,那就完全没问题了?
即使我最初遇到了麻烦,我是否应该努力升级到更高版本的 Npgsql?
我是否应该暂时放弃异步,因为异步在 .NET 世界中可能没有多大区别?
我是否应该放弃 SQLProvider,因为其他 ORM/对象映射器库没有这些问题(例如 Dapper)?
如果不使用 Postgres,我会不会运气更好,因为 SQL Server 在 .NET 世界中得到了更好的支持?
或者单声道还没有准备好迎接黄金时段,我最好让服务器在 Windows 机器上运行?
open FSharp.Data.Sql
open System.Transactions
let [<Literal>] connectionString = "Host=localhost;Database=example;Username=postgres;Password=postgres;Enlist=true"
let [<Literal>] resolutionPath = __SOURCE_DIRECTORY__ + @"..\packages\Npgsql.3.1.0\lib\net451"
type sql = SqlDataProvider<Common.DatabaseProviderTypes.POSTGRESQL,
connectionString,
ResolutionPath = resolutionPath,
UseOptionTypes = true>
let withTransaction fn =
async {
use transaction = new TransactionScope (TransactionScopeAsyncFlowOption.Enabled)
let ctx = sql.GetDataContext ()
let! result = fn ctx
transaction.Complete ()
return result
}
let createPerson (ctx: sql.dataContext) =
let row = ctx.Public.Person.Create ()
row.Id <- 1
row.Name <- "Hello"
ctx.SubmitUpdatesAsync ()
let editPerson (ctx: sql.dataContext) =
query {
for row in ctx.Public.Person do
take 1
}
|> Seq.iter (fun row -> row.Name <- "Hi there!")
|> ctx.SubmitUpdatesAsync
let deletePerson (ctx: sql.dataContext) =
query {
for row in ctx.Public.Person do
take 1
}
|> Seq.iter (fun row -> row.Delete ())
|> ctx.SubmitUpdatesAsync
let run (ctx: sql.dataContext) =
async {
do! createPerson ctx
do! editPerson ctx
do! deletePerson ctx
}
[<EntryPoint>]
let main argv =
withTransaction run
|> Async.RunSynchronously
|> ignore
0
The method or operation is not implemented.
at System.Transactions.TransactionInterop.GetTransmitterPropagationToken (System.Transactions.Transaction transaction) [0x00000] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-06/external/bockbuild/builds/mono-x64/mcs/class/System.Transactions/System.Transactions/TransactionInterop.cs:57
at Npgsql.NpgsqlPromotableSinglePhaseNotification.Enlist (System.Transactions.Transaction tx) [0x00070] in <301d14fab821450fa5cc07ec7c940a17>:0
at Npgsql.NpgsqlConnection.OpenInternal () [0x0012c] in <301d14fab821450fa5cc07ec7c940a17>:0
at Npgsql.NpgsqlConnection.Open () [0x00000] in <301d14fab821450fa5cc07ec7c940a17>:0
at FSharp.Data.Sql.Common.Sql.connect[a] (System.Data.IDbConnection con, Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] f) [0x00009] in <5d776594de6dfdbfa74503839465775d>:0
at FSharp.Data.Sql.Providers.PostgresqlProvider.FSharp-Data-Sql-Common-ISqlProvider-GetColumns (System.Data.IDbConnection con, FSharp.Data.Sql.Schema.Table table) [0x000ae] in <5d776594de6dfdbfa74503839465775d>:0
at FSharp.Data.Sql.Runtime.SqlDataContext.FSharp-Data-Sql-Common-ISqlDataContext-CreateEntity (System.String tableName) [0x0001f] in <5d776594de6dfdbfa74503839465775d>:0
at Program.createPerson (System.Object ctx) [0x00000] in /Users/max/dev2/TransactionMinimalReproduction/TransactionMinimalReproduction/Program.fs:22
at [email protected] (Microsoft.FSharp.Core.Unit unitVar) [0x00000] in /Users/max/dev2/TransactionMinimalReproduction/TransactionMinimalReproduction/Program.fs:45
at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult] (Microsoft.FSharp.Control.AsyncActivation`1[T] ctxt, TResult result1, Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] part2) [0x00005] in <039b17603f7a807e0eeaa652dc64c784>:0
at Program+withTransaction@16-4[a].Invoke (Microsoft.FSharp.Control.AsyncActivation`1[T] ctxt) [0x00000] in /Users/max/dev2/TransactionMinimalReproduction/TransactionMinimalReproduction/Program.fs:16
at Microsoft.FSharp.Control.Trampoline.Execute (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] firstAction) [0x00020] in <039b17603f7a807e0eeaa652dc64c784>:0
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.FSharp.Control.AsyncResult`1[T].Commit () [0x0002c] in <039b17603f7a807e0eeaa652dc64c784>:0
at Microsoft.FSharp.Control.AsyncPrimitives.RunSynchronouslyInCurrentThread[a] (System.Threading.CancellationToken cancellationToken, Microsoft.FSharp.Control.FSharpAsync`1[T] computation) [0x00028] in <039b17603f7a807e0eeaa652dc64c784>:0
at Microsoft.FSharp.Control.AsyncPrimitives.RunSynchronously[T] (System.Threading.CancellationToken cancellationToken, Microsoft.FSharp.Control.FSharpAsync`1[T] computation, Microsoft.FSharp.Core.FSharpOption`1[T] timeout) [0x00013] in <039b17603f7a807e0eeaa652dc64c784>:0
at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously[T] (Microsoft.FSharp.Control.FSharpAsync`1[T] computation, Microsoft.FSharp.Core.FSharpOption`1[T] timeout, Microsoft.FSharp.Core.FSharpOption`1[T] cancellationToken) [0x0006e] in <039b17603f7a807e0eeaa652dc64c784>:0
at Program.main (System.String[] argv) [0x00000] in /Users/max/dev2/TransactionMinimalReproduction/TransactionMinimalReproduction/Program.fs:52
任何和所有帮助将不胜感激。
Async/await 可能返回到一个新线程,也可能返回到同一个线程。因此,这破坏了旧的 .NET Framework 事务,这些事务不再通过线程继续进行。因此,在 .NET 4.5.1 中,他们创建了一个选项
Transactions.TransactionScope(Transactions.TransactionScopeAsyncFlowOption.Enabled)
来支持跨多个线程的事务。遗憾的是,该选项从未在 Mono 中正确实现(只是出于兼容性原因复制了参数)。那时的焦点已经是 .NET Core。
因此,为了保持其可靠性,不要在 Mono 上使用 Async(至少不在生产环境中)。