Skip to content

Commit d36824c

Browse files
Add GetOrAdd on the session factory factory
fix nhibernate#3657
1 parent 916c6dc commit d36824c

File tree

4 files changed

+108
-3
lines changed

4 files changed

+108
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH3657
4+
{
5+
class Entity
6+
{
7+
public virtual Guid Id { get; set; }
8+
public virtual string Name { get; set; }
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using log4net;
2+
using NHibernate.Cfg;
3+
using NHibernate.Impl;
4+
using NUnit.Framework;
5+
6+
namespace NHibernate.Test.NHSpecificTest.GH3657
7+
{
8+
[TestFixture]
9+
public class Fixture
10+
{
11+
private static readonly ILog _log = LogManager.GetLogger(typeof(Fixture));
12+
private const string TestSessionFactoryName = "TestName";
13+
14+
private Configuration _cfg;
15+
private ISessionFactory _builtSessionFactory;
16+
17+
[OneTimeSetUp]
18+
public void TestFixtureSetUp()
19+
{
20+
_cfg = TestConfigurationHelper.GetDefaultConfiguration();
21+
var type = GetType();
22+
_cfg.AddResource(type.Namespace + ".Mappings.hbm.xml", type.Assembly);
23+
_cfg.SetProperty(Environment.SessionFactoryName, TestSessionFactoryName);
24+
}
25+
26+
[TearDown]
27+
public void TearDown()
28+
{
29+
_builtSessionFactory?.Dispose();
30+
_builtSessionFactory = null;
31+
}
32+
33+
private ISessionFactory SessionFactoryBuilder()
34+
{
35+
Assert.That(_builtSessionFactory, Is.Null, "SessionFactory was already built");
36+
37+
_builtSessionFactory = _cfg.BuildSessionFactory();
38+
_log.Info("Successfully built session factory");
39+
40+
return _builtSessionFactory;
41+
}
42+
43+
[Test]
44+
public void GetOrAddTwice()
45+
{
46+
var factory = SessionFactoryObjectFactory.GetOrBuildNamedInstance(TestSessionFactoryName, SessionFactoryBuilder);
47+
Assert.That(factory, Is.Not.Null, "Failed to get the factory once");
48+
49+
var factory2 = SessionFactoryObjectFactory.GetOrBuildNamedInstance(TestSessionFactoryName, SessionFactoryBuilder);
50+
Assert.That(factory2, Is.Not.Null, "Failed to get the factory twice");
51+
Assert.That(factory, Is.SameAs(factory2), "The two factories should be the same");
52+
}
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
3+
namespace="NHibernate.Test.NHSpecificTest.GH3657">
4+
5+
<class name="Entity">
6+
<id name="Id" generator="guid.comb"/>
7+
<property name="Name"/>
8+
</class>
9+
10+
</hibernate-mapping>

src/NHibernate/Impl/SessionFactoryObjectFactory.cs

+34-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Runtime.CompilerServices;
34

@@ -78,16 +79,46 @@ public static void RemoveInstance(string uid, string name, IDictionary<string, s
7879
}
7980

8081
/// <summary>
81-
/// Returns a Named Instance of the SessionFactory from the local "cache" identified by name.
82+
/// Get an instance of the SessionFactory from the local "cache" identified by name if it
83+
/// exists, otherwise run the provided factory and return its result.
8284
/// </summary>
8385
/// <param name="name">The name of the ISessionFactory.</param>
86+
/// <param name="instanceBuilder">The ISessionFactory factory to use if the instance is not
87+
/// found.</param>
8488
/// <returns>An instantiated ISessionFactory.</returns>
89+
/// <remarks>
90+
/// <para>It is the caller responsibility to ensure <paramref name="instanceBuilder"/>
91+
/// will add and yield a session factory of the requested <paramref name="name"/>.</para>
92+
/// <para>If the session factory instantiation is performed concurrently outside of a
93+
/// <c>GetOrAddNamedInstance</c> call, this method may yield an instance of it still being
94+
/// built, which may lead to threading issues.</para>
95+
/// </remarks>
96+
[MethodImpl(MethodImplOptions.Synchronized)]
97+
public static ISessionFactory GetOrBuildNamedInstance(string name, Func<ISessionFactory> instanceBuilder)
98+
{
99+
if (instanceBuilder == null)
100+
throw new ArgumentNullException(nameof(instanceBuilder));
101+
102+
if (NamedInstances.TryGetValue(name, out var factory))
103+
return factory;
104+
return instanceBuilder();
105+
}
106+
107+
/// <summary>
108+
/// Returns a Named Instance of the SessionFactory from the local "cache" identified by name.
109+
/// </summary>
110+
/// <param name="name">The name of the ISessionFactory.</param>
111+
/// <returns>An ISessionFactory if found, <see langword="null" /> otherwise.</returns>
112+
/// <remarks>If the session factory instantiation is performed concurrently, this method
113+
/// may yield an instance of it still being built, which may lead to threading issues.
114+
/// Use <see cref="GetOrBuildNamedInstance(string, Func{ISessionFactory})" /> to get or
115+
/// built the session factory in such case.</remarks>
85116
[MethodImpl(MethodImplOptions.Synchronized)]
86117
public static ISessionFactory GetNamedInstance(string name)
87118
{
88119
log.Debug("lookup: name={0}", name);
89120
ISessionFactory factory;
90-
bool found=NamedInstances.TryGetValue(name, out factory);
121+
bool found = NamedInstances.TryGetValue(name, out factory);
91122
if (!found)
92123
{
93124
log.Warn("Not found: {0}", name);
@@ -99,7 +130,7 @@ public static ISessionFactory GetNamedInstance(string name)
99130
/// Returns an Instance of the SessionFactory from the local "cache" identified by UUID.
100131
/// </summary>
101132
/// <param name="uid">The identifier of the ISessionFactory.</param>
102-
/// <returns>An instantiated ISessionFactory.</returns>
133+
/// <returns>An ISessionFactory if found, <see langword="null" /> otherwise.</returns>
103134
[MethodImpl(MethodImplOptions.Synchronized)]
104135
public static ISessionFactory GetInstance(string uid)
105136
{

0 commit comments

Comments
 (0)