Skip to content

extend session events to cover raw connections and commands - #493

Open
dagreen-st wants to merge 2 commits into
master-servicetitanfrom
async-events
Open

extend session events to cover raw connections and commands#493
dagreen-st wants to merge 2 commits into
master-servicetitanfrom
async-events

Conversation

@dagreen-st

@dagreen-st dagreen-st commented Jul 31, 2026

Copy link
Copy Markdown

Summary

  • Adds a DbConnectionOpened event on SessionEventAccessor, fired once per genuine Closed→Open connection transition
  • Adds an internal EventNotifyingDbCommand wrapper so DirectSqlAccessor.CreateCommand() raises the same DbCommandExecuting/DbCommandExecuted events the ORM's own commands do
  • Adds GetConnectionAsync on IDirectSqlService/DirectSqlAccessor, backed by the existing async prepare path, plus an awaitable raw-connection-accessed hook

Related:

Test plan

  • dotnet build on Xtensive.Orm and Xtensive.Orm.Tests
  • New tests (DbConnectionOpenedEventTest, EventNotifyingDbCommandTest, DirectSqlGetConnectionAsyncTest) pass against a local SQL Server container
  • Full Xtensive.Orm.Tests.Storage suite run before/after: identical pre-existing failures, no regressions

Comment thread Orm/Xtensive.Orm/Orm/Providers/Interfaces/IDirectSqlService.cs
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.
@dagreen-st
dagreen-st requested a review from SergeiPavlov July 31, 2026 23:00
@dagreen-st
dagreen-st marked this pull request as ready for review July 31, 2026 23:01
Prepare();
return connection.UnderlyingConnection;
var underlyingConnection = connection.UnderlyingConnection;
Session.Events.NotifyRawConnectionAccessed(underlyingConnection);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we already have DbConnectionAccessorExtension which already covers NotifyRawConnectionAccessed event. See SqlConnection class.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@SergeiPavlov SergeiPavlov Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it readonly record struct for simplified syntax


public override ValueTask DisposeAsync() => innerCommand.DisposeAsync();

public EventNotifyingDbCommand(Session session, DbCommand innerCommand)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Primary Constructor syntax for new classes


using System;
using System.Data.Common;
using System.Threading;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var callOrder = new List<int>();
List<int> callOrder = [];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants