extend session events to cover raw connections and commands - #493
extend session events to cover raw connections and commands#493dagreen-st wants to merge 2 commits into
Conversation
The ORM's own DbCommandExecuting/Executed events only fire for commands run through its own execution path, and there was no hook for a connection's first genuine open or for a raw connection handed out without a DbCommand. Code using DirectSqlAccessor, or needing the raw connection directly, had no way to observe any of this. Adds a DbConnectionOpened event fired on the Closed to Open transition, an EventNotifyingDbCommand wrapper so DirectSqlAccessor.CreateCommand() raises the same execute events ORM commands do, and an async GetConnectionAsync with an awaitable raw-connection-accessed hook so callers that need the connection itself can hook in without blocking a thread.
| Prepare(); | ||
| return connection.UnderlyingConnection; | ||
| var underlyingConnection = connection.UnderlyingConnection; | ||
| Session.Events.NotifyRawConnectionAccessed(underlyingConnection); |
There was a problem hiding this comment.
Looks like we already have DbConnectionAccessorExtension which already covers NotifyRawConnectionAccessed event. See SqlConnection class.
There was a problem hiding this comment.
Looked into this, and it looks like it's not going to work: ConnectionOpened fires once, inside SqlConnection.Open()/OpenAsync(), gated by EnsureConnectionIsOpen's connection.State != ConnectionState.Open check. Once a connection is already open, calling .Connection/GetConnectionAsync() again never re-fires it. We need a notification on every hand-out of the raw connection.
| /// <summary> | ||
| /// Event args for <see cref="SessionEventAccessor.DbConnectionOpened"/>. | ||
| /// </summary> | ||
| public readonly struct DbConnectionEventArgs |
There was a problem hiding this comment.
Make it readonly record struct for simplified syntax
|
|
||
| public override ValueTask DisposeAsync() => innerCommand.DisposeAsync(); | ||
|
|
||
| public EventNotifyingDbCommand(Session session, DbCommand innerCommand) |
There was a problem hiding this comment.
Use Primary Constructor syntax for new classes
|
|
||
| using System; | ||
| using System.Data.Common; | ||
| using System.Threading; |
There was a problem hiding this comment.
Remove these usings
They are added by default
| using (var session = Domain.OpenSession()) | ||
| using (var transaction = session.OpenTransaction()) { | ||
| var callOrder = new System.Collections.Generic.List<int>(); | ||
| var callOrder = new List<int>(); |
There was a problem hiding this comment.
| var callOrder = new List<int>(); | |
| List<int> callOrder = []; |
Summary
DbConnectionOpenedevent onSessionEventAccessor, fired once per genuine Closed→Open connection transitionEventNotifyingDbCommandwrapper soDirectSqlAccessor.CreateCommand()raises the sameDbCommandExecuting/DbCommandExecutedevents the ORM's own commands doGetConnectionAsynconIDirectSqlService/DirectSqlAccessor, backed by the existing async prepare path, plus an awaitable raw-connection-accessed hookRelated:
Test plan
dotnet buildonXtensive.OrmandXtensive.Orm.TestsDbConnectionOpenedEventTest,EventNotifyingDbCommandTest,DirectSqlGetConnectionAsyncTest) pass against a local SQL Server containerXtensive.Orm.Tests.Storagesuite run before/after: identical pre-existing failures, no regressions