Skip to content

Commit ff232be

Browse files
committed
Fix serialization
1 parent e3bba87 commit ff232be

File tree

7 files changed

+142
-40
lines changed

7 files changed

+142
-40
lines changed

src/NHibernate.Test/Async/MultiTenancy/DatabaseStrategyNoDbSpecificFixture.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
using System;
1212
using System.Data.SqlClient;
13+
using System.IO;
1314
using System.Linq;
15+
using System.Runtime.Serialization.Formatters.Binary;
1416
using NHibernate.Cfg;
1517
using NHibernate.Cfg.MappingSchema;
1618
using NHibernate.Connection;
@@ -19,6 +21,7 @@
1921
using NHibernate.Linq;
2022
using NHibernate.Mapping.ByCode;
2123
using NHibernate.MultiTenancy;
24+
using NHibernate.Util;
2225
using NUnit.Framework;
2326

2427
namespace NHibernate.Test.MultiTenancy
@@ -36,6 +39,12 @@ protected override void Configure(Configuration configuration)
3639
base.Configure(configuration);
3740
}
3841

42+
private static void ValidateSqlServerConnectionAppName(ISession s, string tenantId)
43+
{
44+
var builder = new SqlConnectionStringBuilder(s.Connection.ConnectionString);
45+
Assert.That(builder.ApplicationName, Is.EqualTo(tenantId));
46+
}
47+
3948
[Test]
4049
public async Task SecondLevelCacheReusedForSameTenantAsync()
4150
{
@@ -107,6 +116,46 @@ public async Task QueryCacheSeparationPerTenantAsync()
107116
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(1));
108117
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0));
109118
}
119+
120+
[Test]
121+
public async Task TenantSessionIsSerializableAndCanBeReconnectedAsync()
122+
{
123+
ISession deserializedSession = null;
124+
using (var sesTen1 = OpenTenantSession("tenant1"))
125+
{
126+
var entity = await (sesTen1.Query<Entity>().WithOptions(x => x.SetCacheable(true)).Where(e => e.Id == _id).SingleOrDefaultAsync());
127+
sesTen1.Disconnect();
128+
deserializedSession = SpoofSerialization(sesTen1);
129+
}
130+
131+
Sfi.Statistics.Clear();
132+
using (deserializedSession)
133+
{
134+
deserializedSession.Reconnect();
135+
var entity = await (deserializedSession.GetAsync<Entity>(_id));
136+
if (IsSqlServerDialect)
137+
ValidateSqlServerConnectionAppName(deserializedSession, "tenant1");
138+
}
139+
140+
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(0));
141+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0));
142+
}
143+
144+
private ISession SpoofSerialization(ISession session)
145+
{
146+
var formatter = new BinaryFormatter
147+
{
148+
#if !NETFX
149+
SurrogateSelector = new SerializationHelper.SurrogateSelector()
150+
#endif
151+
};
152+
MemoryStream stream = new MemoryStream();
153+
formatter.Serialize(stream, session);
154+
155+
stream.Position = 0;
156+
157+
return (ISession) formatter.Deserialize(stream);
158+
}
110159

111160
private ISession OpenTenantSession(string tenantId)
112161
{
@@ -118,6 +167,8 @@ private TenantConfiguration GetTenantConfig(string tenantId)
118167
return new TenantConfiguration(new TestTenantConnectionProvider(Sfi, tenantId));
119168
}
120169

170+
private bool IsSqlServerDialect => Sfi.Dialect is MsSql2005Dialect;
171+
121172
#region Test Setup
122173

123174
protected override HbmMapping GetMappings()

src/NHibernate.Test/MultiTenancy/DatabaseStrategyNoDbSpecificFixture.cs

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Data.SqlClient;
3+
using System.IO;
34
using System.Linq;
5+
using System.Runtime.Serialization.Formatters.Binary;
46
using NHibernate.Cfg;
57
using NHibernate.Cfg.MappingSchema;
68
using NHibernate.Connection;
@@ -9,6 +11,7 @@
911
using NHibernate.Linq;
1012
using NHibernate.Mapping.ByCode;
1113
using NHibernate.MultiTenancy;
14+
using NHibernate.Util;
1215
using NUnit.Framework;
1316

1417
namespace NHibernate.Test.MultiTenancy
@@ -44,20 +47,24 @@ public void ShouldThrowWithNoConnectionAccess()
4447
[Test]
4548
public void DifferentConnectionStringForDifferentTenants()
4649
{
47-
if (!(Sfi.Dialect is MsSql2005Dialect))
50+
if (!IsSqlServerDialect)
4851
Assert.Ignore("MSSqlServer specific test");
4952

5053
using(var session1 = OpenTenantSession("tenant1"))
5154
using (var session2 = OpenTenantSession("tenant2"))
5255
{
5356
Assert.That(session1.Connection.ConnectionString, Is.Not.EqualTo(session2.Connection.ConnectionString));
54-
var builder1 = new SqlConnectionStringBuilder(session1.Connection.ConnectionString);
55-
var builder2 = new SqlConnectionStringBuilder(session2.Connection.ConnectionString);
56-
Assert.That(builder1.ApplicationName, Is.EqualTo("tenant1"));
57-
Assert.That(builder2.ApplicationName, Is.EqualTo("tenant2"));
57+
ValidateSqlServerConnectionAppName(session1, "tenant1");
58+
ValidateSqlServerConnectionAppName(session2, "tenant2");
5859
}
5960
}
6061

62+
private static void ValidateSqlServerConnectionAppName(ISession s, string tenantId)
63+
{
64+
var builder = new SqlConnectionStringBuilder(s.Connection.ConnectionString);
65+
Assert.That(builder.ApplicationName, Is.EqualTo(tenantId));
66+
}
67+
6168
[Test]
6269
public void SecondLevelCacheReusedForSameTenant()
6370
{
@@ -129,6 +136,46 @@ public void QueryCacheSeparationPerTenant()
129136
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(1));
130137
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0));
131138
}
139+
140+
[Test]
141+
public void TenantSessionIsSerializableAndCanBeReconnected()
142+
{
143+
ISession deserializedSession = null;
144+
using (var sesTen1 = OpenTenantSession("tenant1"))
145+
{
146+
var entity = sesTen1.Query<Entity>().WithOptions(x => x.SetCacheable(true)).Where(e => e.Id == _id).SingleOrDefault();
147+
sesTen1.Disconnect();
148+
deserializedSession = SpoofSerialization(sesTen1);
149+
}
150+
151+
Sfi.Statistics.Clear();
152+
using (deserializedSession)
153+
{
154+
deserializedSession.Reconnect();
155+
var entity = deserializedSession.Get<Entity>(_id);
156+
if (IsSqlServerDialect)
157+
ValidateSqlServerConnectionAppName(deserializedSession, "tenant1");
158+
}
159+
160+
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(0));
161+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0));
162+
}
163+
164+
private ISession SpoofSerialization(ISession session)
165+
{
166+
var formatter = new BinaryFormatter
167+
{
168+
#if !NETFX
169+
SurrogateSelector = new SerializationHelper.SurrogateSelector()
170+
#endif
171+
};
172+
MemoryStream stream = new MemoryStream();
173+
formatter.Serialize(stream, session);
174+
175+
stream.Position = 0;
176+
177+
return (ISession) formatter.Deserialize(stream);
178+
}
132179

133180
private ISession OpenTenantSession(string tenantId)
134181
{
@@ -140,6 +187,8 @@ private TenantConfiguration GetTenantConfig(string tenantId)
140187
return new TenantConfiguration(new TestTenantConnectionProvider(Sfi, tenantId));
141188
}
142189

190+
private bool IsSqlServerDialect => Sfi.Dialect is MsSql2005Dialect;
191+
143192
#region Test Setup
144193

145194
protected override HbmMapping GetMappings()
@@ -193,6 +242,8 @@ protected override void OnSetUp()
193242

194243
#endregion Test Setup
195244
}
245+
246+
[Serializable]
196247
class Entity
197248
{
198249
public virtual Guid Id { get; set; }
@@ -216,16 +267,17 @@ public IConnectionAccess GetConnectionAccess()
216267
}
217268
}
218269

270+
[Serializable]
219271
public class TestTenantConnectionProvider : AbstractMultiTenantConnectionProvider
220272
{
221273
public TestTenantConnectionProvider(ISessionFactoryImplementor sfi, string tenantId)
222274
{
223275
TenantIdentifier = tenantId;
224-
ConnectionProvider = sfi.ConnectionProvider;
276+
SessionFactory = sfi;
225277
TenantConnectionString = sfi.ConnectionProvider.GetConnectionString();
226278
if (sfi.Dialect is MsSql2005Dialect)
227279
{
228-
var stringBuilder = new SqlConnectionStringBuilder(ConnectionProvider.GetConnectionString());
280+
var stringBuilder = new SqlConnectionStringBuilder(sfi.ConnectionProvider.GetConnectionString());
229281
stringBuilder.ApplicationName = tenantId;
230282
TenantConnectionString = stringBuilder.ToString();
231283
}
@@ -235,6 +287,6 @@ public TestTenantConnectionProvider(ISessionFactoryImplementor sfi, string tenan
235287

236288
public override string TenantIdentifier { get; }
237289

238-
protected override IConnectionProvider ConnectionProvider { get; }
290+
protected override ISessionFactoryImplementor SessionFactory { get; }
239291
}
240292
}

src/NHibernate/AdoNet/ConnectionManager.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public ConnectionManager(
8686
#pragma warning restore 618
8787

8888
{
89-
_connectionAccess = connectionAccess;
89+
_connectionAccess = connectionAccess ?? throw new ArgumentNullException(nameof(connectionAccess));
9090
}
9191

9292
//Since 5.3
@@ -380,6 +380,7 @@ private ConnectionManager(SerializationInfo info, StreamingContext context)
380380
_connectionReleaseMode =
381381
(ConnectionReleaseMode)info.GetValue("connectionReleaseMode", typeof(ConnectionReleaseMode));
382382
_interceptor = (IInterceptor)info.GetValue("interceptor", typeof(IInterceptor));
383+
_connectionAccess = (IConnectionAccess) info.GetValue("connectionAccess", typeof(IConnectionAccess));
383384
}
384385

385386
[SecurityCritical]
@@ -389,6 +390,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context)
389390
info.AddValue("session", Session, typeof(ISessionImplementor));
390391
info.AddValue("connectionReleaseMode", _connectionReleaseMode, typeof(ConnectionReleaseMode));
391392
info.AddValue("interceptor", _interceptor, typeof(IInterceptor));
393+
info.AddValue("connectionAccess", _connectionAccess, typeof(IConnectionAccess));
392394
}
393395

394396
#endregion

src/NHibernate/Async/Impl/AbstractSessionImpl.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,7 @@ protected async Task AfterOperationAsync(bool success, CancellationToken cancell
219219

220220
public abstract Task<int> ExecuteUpdateAsync(IQueryExpression queryExpression, QueryParameters queryParameters, CancellationToken cancellationToken);
221221
}
222-
223-
partial class NonContextualConnectionAccess : IConnectionAccess
222+
partial class NonContextualConnectionAccess : IConnectionAccess
224223
{
225224

226225
public Task<DbConnection> GetConnectionAsync(CancellationToken cancellationToken)
@@ -229,7 +228,7 @@ public Task<DbConnection> GetConnectionAsync(CancellationToken cancellationToken
229228
{
230229
return Task.FromCanceled<DbConnection>(cancellationToken);
231230
}
232-
return _connectionProvider.GetConnectionAsync(cancellationToken);
231+
return _factory.ConnectionProvider.GetConnectionAsync(cancellationToken);
233232
}
234233
}
235234
}

src/NHibernate/Async/MultiTenancy/AbstractMultiTenantConnectionProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System;
1212
using System.Data.Common;
1313
using NHibernate.Connection;
14+
using NHibernate.Engine;
1415
using NHibernate.Util;
1516

1617
namespace NHibernate.MultiTenancy
@@ -19,7 +20,6 @@ namespace NHibernate.MultiTenancy
1920
using System.Threading;
2021
public abstract partial class AbstractMultiTenantConnectionProvider : IMultiTenantConnectionProvider
2122
{
22-
2323
partial class ContextualConnectionAccess : IConnectionAccess
2424
{
2525

@@ -29,7 +29,7 @@ public Task<DbConnection> GetConnectionAsync(CancellationToken cancellationToken
2929
{
3030
return Task.FromCanceled<DbConnection>(cancellationToken);
3131
}
32-
return _connectionProvider.GetConnectionAsync(ConnectionString, cancellationToken);
32+
return _factory.ConnectionProvider.GetConnectionAsync(ConnectionString, cancellationToken);
3333
}
3434
}
3535
}

src/NHibernate/Impl/AbstractSessionImpl.cs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ public abstract partial class AbstractSessionImpl : ISessionImplementor
3636
[NonSerialized]
3737
private IQueryBatch _futureMultiBatch;
3838

39-
private readonly TenantConfiguration _tenantConfiguration;
40-
4139
private bool closed;
4240

4341
/// <summary>Get the current NHibernate transaction.</summary>
@@ -84,11 +82,9 @@ protected internal AbstractSessionImpl(ISessionFactoryImplementor factory, ISess
8482
_flushMode = options.InitialSessionFlushMode;
8583
Interceptor = options.SessionInterceptor ?? EmptyInterceptor.Instance;
8684

87-
if (options is ISessionCreationOptionsWithMultiTenancy multiTenancy)
88-
{
89-
_tenantConfiguration = multiTenancy.TenantConfiguration;
90-
ValidateTenantConfiguration(factory, _tenantConfiguration);
91-
}
85+
TenantConfiguration tenantConfiguration = options is ISessionCreationOptionsWithMultiTenancy multiTenancy ? multiTenancy.TenantConfiguration : null;
86+
ValidateTenantConfiguration(factory, tenantConfiguration);
87+
TenantIdentifier = tenantConfiguration?.TenantIdentifier;
9288

9389
if (options is ISharedSessionCreationOptions sharedOptions && sharedOptions.IsTransactionCoordinatorShared)
9490
{
@@ -107,7 +103,7 @@ protected internal AbstractSessionImpl(ISessionFactoryImplementor factory, ISess
107103
options.UserSuppliedConnection,
108104
options.SessionConnectionReleaseMode,
109105
Interceptor,
110-
_tenantConfiguration?.ConnectionAccess ?? new NonContextualConnectionAccess(_factory.ConnectionProvider),
106+
tenantConfiguration?.ConnectionAccess ?? new NonContextualConnectionAccess(_factory),
111107
options.ShouldAutoJoinTransaction);
112108
}
113109
}
@@ -489,7 +485,7 @@ protected bool IsAlreadyDisposed
489485
/// <inheritdoc />
490486
public virtual bool TransactionInProgress => ConnectionManager.IsInActiveTransaction;
491487

492-
public string TenantIdentifier => _tenantConfiguration?.TenantIdentifier;
488+
public string TenantIdentifier { get; }
493489

494490
#endregion
495491

@@ -695,27 +691,27 @@ public virtual IQueryBatch CreateQueryBatch()
695691
return new QueryBatch(this, false);
696692
}
697693
}
698-
[Serializable]
699694

700-
partial class NonContextualConnectionAccess : IConnectionAccess
695+
[Serializable]
696+
partial class NonContextualConnectionAccess : IConnectionAccess
701697
{
702-
private readonly IConnectionProvider _connectionProvider;
698+
private readonly ISessionFactoryImplementor _factory;
703699

704-
public NonContextualConnectionAccess(IConnectionProvider connectionProvider)
700+
public NonContextualConnectionAccess(ISessionFactoryImplementor factory)
705701
{
706-
_connectionProvider = connectionProvider;
702+
_factory = factory;
707703
}
708704

709705
public DbConnection GetConnection()
710706
{
711-
return _connectionProvider.GetConnection();
707+
return _factory.ConnectionProvider.GetConnection();
712708
}
713709

714710
public void CloseConnection(DbConnection connection)
715711
{
716-
_connectionProvider.CloseConnection(connection);
712+
_factory.ConnectionProvider.CloseConnection(connection);
717713
}
718714

719-
public string ConnectionString => _connectionProvider.GetConnectionString();
715+
public string ConnectionString => _factory.ConnectionProvider.GetConnectionString();
720716
}
721717
}

0 commit comments

Comments
 (0)