Skip to content

Fix ExecuteWorkInIsolation ignores MultiTenancy configuration #2835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Engine;
using NHibernate.Engine.Transaction;
using NHibernate.Linq;
using NHibernate.Mapping.ByCode;
using NHibernate.MultiTenancy;
Expand All @@ -28,6 +29,7 @@
namespace NHibernate.Test.MultiTenancy
{
using System.Threading.Tasks;
using System.Threading;
[TestFixture]
public class DatabaseStrategyNoDbSpecificFixtureAsync : TestCaseMappingByCode
{
Expand Down Expand Up @@ -160,6 +162,18 @@ public async Task TenantSessionIsSerializableAndCanBeReconnectedAsync()
Assert.That(Sfi.Statistics.SecondLevelCacheHitCount, Is.EqualTo(1));
}

[Test]
public async Task TenantIsolatedWorkOpensTenantConnectionAsync()
{
if (!IsSqlServerDialect)
Assert.Ignore("MSSqlServer specific test");

using (var ses = OpenTenantSession("tenant1"))
{
await (Isolater.DoIsolatedWorkAsync(new TenantIsolatatedWork("tenant1"), ses.GetSessionImplementation(), CancellationToken.None));
}
}

private static string GetTenantId(ISession session)
{
return session.GetSessionImplementation().GetTenantIdentifier();
Expand Down
30 changes: 30 additions & 0 deletions src/NHibernate.Test/Async/MultiTenancy/TenantIsolatatedWork.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using System.Data.Common;
using System.Data.SqlClient;
using NHibernate.Engine.Transaction;

namespace NHibernate.Test.MultiTenancy
{
using System.Threading.Tasks;
using System.Threading;
public partial class TenantIsolatatedWork : IIsolatedWork
{

public Task DoWorkAsync(DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken)
{
var builder = new SqlConnectionStringBuilder(connection.ConnectionString);
if (builder.ApplicationName != _tenantName)
return Task.FromException<object>(new HibernateException("Invalid tenant connection"));
return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Engine;
using NHibernate.Engine.Transaction;
using NHibernate.Linq;
using NHibernate.Mapping.ByCode;
using NHibernate.MultiTenancy;
Expand Down Expand Up @@ -229,6 +230,18 @@ public void TenantSessionIsSerializableAndCanBeReconnected()
Assert.That(Sfi.Statistics.SecondLevelCacheHitCount, Is.EqualTo(1));
}

[Test]
public void TenantIsolatedWorkOpensTenantConnection()
{
if (!IsSqlServerDialect)
Assert.Ignore("MSSqlServer specific test");

using (var ses = OpenTenantSession("tenant1"))
{
Isolater.DoIsolatedWork(new TenantIsolatatedWork("tenant1"), ses.GetSessionImplementation());
}
}

private static string GetTenantId(ISession session)
{
return session.GetSessionImplementation().GetTenantIdentifier();
Expand Down
23 changes: 23 additions & 0 deletions src/NHibernate.Test/MultiTenancy/TenantIsolatatedWork.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Data.Common;
using System.Data.SqlClient;
using NHibernate.Engine.Transaction;

namespace NHibernate.Test.MultiTenancy
{
public partial class TenantIsolatatedWork : IIsolatedWork
{
private readonly string _tenantName;

public TenantIsolatatedWork(string tenantName)
{
_tenantName = tenantName;
}

public void DoWork(DbConnection connection, DbTransaction transaction)
{
var builder = new SqlConnectionStringBuilder(connection.ConnectionString);
if (builder.ApplicationName != _tenantName)
throw new HibernateException("Invalid tenant connection");
}
}
}
15 changes: 14 additions & 1 deletion src/NHibernate/AdoNet/ConnectionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,19 @@ private void CloseConnection()
_connection = null;
}

/// <summary>
/// Get a new opened connection. The caller is responsible for closing it.
/// </summary>
/// <returns>An opened connection.</returns>
public DbConnection GetNewConnection()
{
return _connectionAccess.GetConnection();
}

/// <summary>
/// Get the managed connection.
/// </summary>
/// <returns>An opened connection.</returns>
public DbConnection GetConnection()
{
if (!_allowConnectionUsage)
Expand Down Expand Up @@ -254,7 +267,7 @@ public DbConnection GetConnection()
{
if (_ownConnection)
{
_connection = _connectionAccess.GetConnection();
_connection = GetNewConnection();
// Will fail if the connection is already enlisted in another transaction.
// Probable case: nested transaction scope with connection auto-enlistment enabled.
// That is an user error.
Expand Down
21 changes: 20 additions & 1 deletion src/NHibernate/Async/AdoNet/ConnectionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ namespace NHibernate.AdoNet
public partial class ConnectionManager : ISerializable, IDeserializationCallback
{

/// <summary>
/// Get a new opened connection. The caller is responsible for closing it.
/// </summary>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
/// <returns>An opened connection.</returns>
public Task<DbConnection> GetNewConnectionAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<DbConnection>(cancellationToken);
}
return _connectionAccess.GetConnectionAsync(cancellationToken);
}

/// <summary>
/// Get the managed connection.
/// </summary>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
/// <returns>An opened connection.</returns>
public Task<DbConnection> GetConnectionAsync(CancellationToken cancellationToken)
{
if (!_allowConnectionUsage)
Expand Down Expand Up @@ -62,7 +81,7 @@ async Task<DbConnection> InternalGetConnectionAsync()
{
if (_ownConnection)
{
_connection = await (_connectionAccess.GetConnectionAsync(cancellationToken)).ConfigureAwait(false);
_connection = await (GetNewConnectionAsync(cancellationToken)).ConfigureAwait(false);
// Will fail if the connection is already enlisted in another transaction.
// Probable case: nested transaction scope with connection auto-enlistment enabled.
// That is an user error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async Task InternalExecuteWorkInIsolationAsync()
// since SQLite only allows one connection to the database.
connection = session.Factory.Dialect is SQLiteDialect
? session.Connection
: await (session.Factory.ConnectionProvider.GetConnectionAsync(cancellationToken)).ConfigureAwait(false);
: await (session.ConnectionManager.GetNewConnectionAsync(cancellationToken)).ConfigureAwait(false);

if (transacted)
{
Expand Down
2 changes: 1 addition & 1 deletion src/NHibernate/Transaction/AdoNetTransactionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public virtual void ExecuteWorkInIsolation(ISessionImplementor session, IIsolate
// since SQLite only allows one connection to the database.
connection = session.Factory.Dialect is SQLiteDialect
? session.Connection
: session.Factory.ConnectionProvider.GetConnection();
: session.ConnectionManager.GetNewConnection();

if (transacted)
{
Expand Down