Skip to content

Commit 46e0634

Browse files
committed
MultiTenancy: Implement tenant per Database strategy
1 parent 151194f commit 46e0634

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+942
-40
lines changed

src/NHibernate.Test/Async/CacheTest/CacheFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public async Task TestSimpleCacheAsync()
2929

3030
private CacheKey CreateCacheKey(string text)
3131
{
32-
return new CacheKey(text, NHibernateUtil.String, "Foo", null);
32+
return new CacheKey(text, NHibernateUtil.String, "Foo", null, null);
3333
}
3434

3535
public async Task DoTestCacheAsync(ICacheProvider cacheProvider, CancellationToken cancellationToken = default(CancellationToken))

src/NHibernate.Test/Async/DebugSessionFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using NHibernate.Id;
2727
using NHibernate.Impl;
2828
using NHibernate.Metadata;
29+
using NHibernate.MultiTenancy;
2930
using NHibernate.Persister.Collection;
3031
using NHibernate.Persister.Entity;
3132
using NHibernate.Proxy;

src/NHibernate.Test/Async/FilterTest/DynamicFilterTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public async Task SecondLevelCachedCollectionsFilteringAsync()
4545
var persister = Sfi
4646
.GetCollectionPersister(typeof(Salesperson).FullName + ".Orders");
4747
var cacheKey =
48-
new CacheKey(testData.steveId, persister.KeyType, persister.Role, Sfi);
48+
new CacheKey(testData.steveId, persister.KeyType, persister.Role, Sfi, null);
4949
CollectionCacheEntry cachedData;
5050

5151
using (var session = OpenSession())
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System;
12+
using System.Data.SqlClient;
13+
using System.Linq;
14+
using NHibernate.Cfg;
15+
using NHibernate.Cfg.MappingSchema;
16+
using NHibernate.Connection;
17+
using NHibernate.Dialect;
18+
using NHibernate.Engine;
19+
using NHibernate.Linq;
20+
using NHibernate.Mapping.ByCode;
21+
using NHibernate.MultiTenancy;
22+
using NUnit.Framework;
23+
24+
namespace NHibernate.Test.MultiTenancy
25+
{
26+
using System.Threading.Tasks;
27+
[TestFixture]
28+
public class DatabaseStrategyNoDbSpecificFixtureAsync : TestCaseMappingByCode
29+
{
30+
private Guid _id;
31+
32+
protected override void Configure(Configuration configuration)
33+
{
34+
configuration.Properties[Cfg.Environment.MultiTenant] = MultiTenancyStrategy.Database.ToString();
35+
configuration.Properties[Cfg.Environment.GenerateStatistics] = true.ToString();
36+
base.Configure(configuration);
37+
}
38+
39+
[Test]
40+
public async Task SecondLevelCacheReusedForSameTenantAsync()
41+
{
42+
using (var sesTen1 = OpenTenantSession("tenant1"))
43+
{
44+
var entity = await (sesTen1.GetAsync<Entity>(_id));
45+
}
46+
47+
Sfi.Statistics.Clear();
48+
using (var sesTen2 = OpenTenantSession("tenant1"))
49+
{
50+
var entity = await (sesTen2.GetAsync<Entity>(_id));
51+
}
52+
53+
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(0));
54+
Assert.That(Sfi.Statistics.SecondLevelCacheHitCount, Is.EqualTo(1));
55+
}
56+
57+
[Test]
58+
public async Task SecondLevelCacheSeparationPerTenantAsync()
59+
{
60+
using (var sesTen1 = OpenTenantSession("tenant1"))
61+
{
62+
var entity = await (sesTen1.GetAsync<Entity>(_id));
63+
}
64+
65+
Sfi.Statistics.Clear();
66+
using (var sesTen2 = OpenTenantSession("tenant2"))
67+
{
68+
var entity = await (sesTen2.GetAsync<Entity>(_id));
69+
}
70+
71+
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(1));
72+
Assert.That(Sfi.Statistics.SecondLevelCacheHitCount, Is.EqualTo(0));
73+
}
74+
75+
[Test]
76+
public async Task QueryCacheReusedForSameTenantAsync()
77+
{
78+
using (var sesTen1 = OpenTenantSession("tenant1"))
79+
{
80+
var entity = await (sesTen1.Query<Entity>().WithOptions(x => x.SetCacheable(true)).Where(e => e.Id == _id).SingleOrDefaultAsync());
81+
}
82+
83+
Sfi.Statistics.Clear();
84+
using (var sesTen2 = OpenTenantSession("tenant1"))
85+
{
86+
var entity = await (sesTen2.Query<Entity>().WithOptions(x => x.SetCacheable(true)).Where(e => e.Id == _id).SingleOrDefaultAsync());
87+
}
88+
89+
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(0));
90+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1));
91+
}
92+
93+
[Test]
94+
public async Task QueryCacheSeparationPerTenantAsync()
95+
{
96+
using (var sesTen1 = OpenTenantSession("tenant1"))
97+
{
98+
var entity = await (sesTen1.Query<Entity>().WithOptions(x => x.SetCacheable(true)).Where(e => e.Id == _id).SingleOrDefaultAsync());
99+
}
100+
101+
Sfi.Statistics.Clear();
102+
using (var sesTen2 = OpenTenantSession("tenant2"))
103+
{
104+
var entity = await (sesTen2.Query<Entity>().WithOptions(x => x.SetCacheable(true)).Where(e => e.Id == _id).SingleOrDefaultAsync());
105+
}
106+
107+
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(1));
108+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0));
109+
}
110+
111+
private ISession OpenTenantSession(string tenantId)
112+
{
113+
return Sfi.WithOptions().TenantConfiguration(GetTenantConfig(tenantId)).OpenSession();
114+
}
115+
116+
private TenantConfiguration GetTenantConfig(string tenantId)
117+
{
118+
return new TenantConfiguration(new TestTenantConnectionProvider(Sfi, tenantId));
119+
}
120+
121+
#region Test Setup
122+
123+
protected override HbmMapping GetMappings()
124+
{
125+
var mapper = new ModelMapper();
126+
127+
mapper.Class<Entity>(
128+
rc =>
129+
{
130+
rc.Cache(m => m.Usage(CacheUsage.NonstrictReadWrite));
131+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
132+
rc.Property(x => x.Name);
133+
});
134+
135+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
136+
}
137+
138+
protected override ISession OpenSession()
139+
{
140+
return OpenTenantSession("defaultTenant");
141+
}
142+
143+
protected override void OnTearDown()
144+
{
145+
using (ISession session = OpenSession())
146+
using (ITransaction transaction = session.BeginTransaction())
147+
{
148+
session.Delete("from System.Object");
149+
150+
session.Flush();
151+
transaction.Commit();
152+
}
153+
}
154+
155+
protected override void OnSetUp()
156+
{
157+
using (var session = OpenSession())
158+
using (var transaction = session.BeginTransaction())
159+
{
160+
var e1 = new Entity {Name = "Bob"};
161+
session.Save(e1);
162+
163+
var e2 = new Entity {Name = "Sally"};
164+
session.Save(e2);
165+
166+
session.Flush();
167+
transaction.Commit();
168+
_id = e1.Id;
169+
}
170+
}
171+
172+
#endregion Test Setup
173+
}
174+
}

src/NHibernate.Test/CacheTest/CacheFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void TestSimpleCache()
1818

1919
private CacheKey CreateCacheKey(string text)
2020
{
21-
return new CacheKey(text, NHibernateUtil.String, "Foo", null);
21+
return new CacheKey(text, NHibernateUtil.String, "Foo", null, null);
2222
}
2323

2424
public void DoTestCache(ICacheProvider cacheProvider)

src/NHibernate.Test/CacheTest/QueryKeyFixture.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ private void QueryKeyFilterDescLikeToCompare(out QueryKey qk, out QueryKey qk1,
3535
f.SetParameter("pLike", "so%");
3636
var fk = new FilterKey(f);
3737
ISet<FilterKey> fks = new HashSet<FilterKey> { fk };
38-
qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null);
38+
qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null, null);
3939

4040
var f1 = new FilterImpl(Sfi.GetFilterDefinition(filterName));
4141
f1.SetParameter("pLike", sameValue ? "so%" : "%ing");
4242
var fk1 = new FilterKey(f1);
4343
fks = new HashSet<FilterKey> { fk1 };
44-
qk1 = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null);
44+
qk1 = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null, null);
4545
}
4646

4747
private void QueryKeyFilterDescValueToCompare(out QueryKey qk, out QueryKey qk1, bool sameValue)
@@ -52,13 +52,13 @@ private void QueryKeyFilterDescValueToCompare(out QueryKey qk, out QueryKey qk1,
5252
f.SetParameter("pDesc", "something").SetParameter("pValue", 10);
5353
var fk = new FilterKey(f);
5454
ISet<FilterKey> fks = new HashSet<FilterKey> { fk };
55-
qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null);
55+
qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null, null);
5656

5757
var f1 = new FilterImpl(Sfi.GetFilterDefinition(filterName));
5858
f1.SetParameter("pDesc", "something").SetParameter("pValue", sameValue ? 10 : 11);
5959
var fk1 = new FilterKey(f1);
6060
fks = new HashSet<FilterKey> { fk1 };
61-
qk1 = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null);
61+
qk1 = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null, null);
6262
}
6363

6464
[Test]
@@ -122,15 +122,15 @@ public void ToStringWithFilters()
122122
f.SetParameter("pLike", "so%");
123123
var fk = new FilterKey(f);
124124
ISet<FilterKey> fks = new HashSet<FilterKey> { fk };
125-
var qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null);
125+
var qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null, null);
126126
Assert.That(qk.ToString(), Does.Contain($"filters: ['{fk}']"), "Like");
127127

128128
filterName = "DescriptionEqualAndValueGT";
129129
f = new FilterImpl(Sfi.GetFilterDefinition(filterName));
130130
f.SetParameter("pDesc", "something").SetParameter("pValue", 10);
131131
fk = new FilterKey(f);
132132
fks = new HashSet<FilterKey> { fk };
133-
qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null);
133+
qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null, null);
134134
Assert.That(qk.ToString(), Does.Contain($"filters: ['{fk}']"), "Value");
135135
}
136136

@@ -148,7 +148,7 @@ public void ToStringWithMoreFilters()
148148
var fvk = new FilterKey(fv);
149149

150150
ISet<FilterKey> fks = new HashSet<FilterKey> { fk, fvk };
151-
var qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null);
151+
var qk = new QueryKey(Sfi, SqlAll, new QueryParameters(), fks, null, null);
152152
Assert.That(qk.ToString(), Does.Contain($"filters: ['{fk}', '{fvk}']"));
153153
}
154154
}

src/NHibernate.Test/DebugSessionFactory.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using NHibernate.Id;
1717
using NHibernate.Impl;
1818
using NHibernate.Metadata;
19+
using NHibernate.MultiTenancy;
1920
using NHibernate.Persister.Collection;
2021
using NHibernate.Persister.Entity;
2122
using NHibernate.Proxy;
@@ -400,7 +401,7 @@ public static ISessionCreationOptions GetCreationOptions(IStatelessSessionBuilde
400401
(ISessionCreationOptions)sessionBuilder;
401402
}
402403

403-
internal class SessionBuilder : ISessionBuilder
404+
internal class SessionBuilder : ISessionBuilder, ISessionCreationOptionsWithMultiTenancy
404405
{
405406
private readonly ISessionBuilder _actualBuilder;
406407
private readonly DebugSessionFactory _debugFactory;
@@ -465,6 +466,12 @@ ISessionBuilder ISessionBuilder<ISessionBuilder>.FlushMode(FlushMode flushMode)
465466
}
466467

467468
#endregion
469+
470+
TenantConfiguration ISessionCreationOptionsWithMultiTenancy.TenantConfiguration
471+
{
472+
get => (_actualBuilder as ISessionCreationOptionsWithMultiTenancy)?.TenantConfiguration;
473+
set => _actualBuilder.TenantConfiguration(value);
474+
}
468475
}
469476

470477
internal class StatelessSessionBuilder : IStatelessSessionBuilder

src/NHibernate.Test/FilterTest/DynamicFilterTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void SecondLevelCachedCollectionsFiltering()
3333
var persister = Sfi
3434
.GetCollectionPersister(typeof(Salesperson).FullName + ".Orders");
3535
var cacheKey =
36-
new CacheKey(testData.steveId, persister.KeyType, persister.Role, Sfi);
36+
new CacheKey(testData.steveId, persister.KeyType, persister.Role, Sfi, null);
3737
CollectionCacheEntry cachedData;
3838

3939
using (var session = OpenSession())

0 commit comments

Comments
 (0)