From 5c1a9fcc5c0ff0658fee28109da53be98bea83c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:08:22 +0200 Subject: [PATCH] Add GetOrBuild on the session factory factory fix #3657 --- .../NHSpecificTest/GH3657/Entity.cs | 10 ++++ .../NHSpecificTest/GH3657/Fixture.cs | 54 +++++++++++++++++++ .../NHSpecificTest/GH3657/Mappings.hbm.xml | 10 ++++ .../Impl/SessionFactoryObjectFactory.cs | 37 +++++++++++-- 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3657/Entity.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3657/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH3657/Mappings.hbm.xml diff --git a/src/NHibernate.Test/NHSpecificTest/GH3657/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH3657/Entity.cs new file mode 100644 index 00000000000..75b7aa6e253 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3657/Entity.cs @@ -0,0 +1,10 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH3657 +{ + class Entity + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3657/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3657/Fixture.cs new file mode 100644 index 00000000000..ec931e6fc80 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3657/Fixture.cs @@ -0,0 +1,54 @@ +using log4net; +using NHibernate.Cfg; +using NHibernate.Impl; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH3657 +{ + [TestFixture] + public class Fixture + { + private static readonly ILog _log = LogManager.GetLogger(typeof(Fixture)); + private const string TestSessionFactoryName = "TestName"; + + private Configuration _cfg; + private ISessionFactory _builtSessionFactory; + + [OneTimeSetUp] + public void TestFixtureSetUp() + { + _cfg = TestConfigurationHelper.GetDefaultConfiguration(); + var type = GetType(); + _cfg.AddResource(type.Namespace + ".Mappings.hbm.xml", type.Assembly); + _cfg.SetProperty(Environment.SessionFactoryName, TestSessionFactoryName); + } + + [TearDown] + public void TearDown() + { + _builtSessionFactory?.Dispose(); + _builtSessionFactory = null; + } + + private ISessionFactory SessionFactoryBuilder() + { + Assert.That(_builtSessionFactory, Is.Null, "SessionFactory was already built"); + + _builtSessionFactory = _cfg.BuildSessionFactory(); + _log.Info("Successfully built session factory"); + + return _builtSessionFactory; + } + + [Test] + public void GetOrAddTwice() + { + var factory = SessionFactoryObjectFactory.GetOrBuildNamedInstance(TestSessionFactoryName, SessionFactoryBuilder); + Assert.That(factory, Is.Not.Null, "Failed to get the factory once"); + + var factory2 = SessionFactoryObjectFactory.GetOrBuildNamedInstance(TestSessionFactoryName, SessionFactoryBuilder); + Assert.That(factory2, Is.Not.Null, "Failed to get the factory twice"); + Assert.That(factory, Is.SameAs(factory2), "The two factories should be the same"); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3657/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH3657/Mappings.hbm.xml new file mode 100644 index 00000000000..0f099887505 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3657/Mappings.hbm.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/NHibernate/Impl/SessionFactoryObjectFactory.cs b/src/NHibernate/Impl/SessionFactoryObjectFactory.cs index 2936f5c2405..fa768d2d8d3 100644 --- a/src/NHibernate/Impl/SessionFactoryObjectFactory.cs +++ b/src/NHibernate/Impl/SessionFactoryObjectFactory.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -78,16 +79,46 @@ public static void RemoveInstance(string uid, string name, IDictionary - /// Returns a Named Instance of the SessionFactory from the local "cache" identified by name. + /// Get an instance of the SessionFactory from the local "cache" identified by name if it + /// exists, otherwise run the provided factory and return its result. /// /// The name of the ISessionFactory. + /// The ISessionFactory factory to use if the instance is not + /// found. /// An instantiated ISessionFactory. + /// + /// It is the caller responsibility to ensure + /// will add and yield a session factory of the requested . + /// If the session factory instantiation is performed concurrently outside of a + /// GetOrAddNamedInstance call, this method may yield an instance of it still being + /// built, which may lead to threading issues. + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public static ISessionFactory GetOrBuildNamedInstance(string name, Func instanceBuilder) + { + if (instanceBuilder == null) + throw new ArgumentNullException(nameof(instanceBuilder)); + + if (NamedInstances.TryGetValue(name, out var factory)) + return factory; + return instanceBuilder(); + } + + /// + /// Returns a Named Instance of the SessionFactory from the local "cache" identified by name. + /// + /// The name of the ISessionFactory. + /// An ISessionFactory if found, otherwise. + /// If the session factory instantiation is performed concurrently, this method + /// may yield an instance of it still being built, which may lead to threading issues. + /// Use to get or + /// built the session factory in such case. [MethodImpl(MethodImplOptions.Synchronized)] public static ISessionFactory GetNamedInstance(string name) { log.Debug("lookup: name={0}", name); ISessionFactory factory; - bool found=NamedInstances.TryGetValue(name, out factory); + bool found = NamedInstances.TryGetValue(name, out factory); if (!found) { log.Warn("Not found: {0}", name); @@ -99,7 +130,7 @@ public static ISessionFactory GetNamedInstance(string name) /// Returns an Instance of the SessionFactory from the local "cache" identified by UUID. /// /// The identifier of the ISessionFactory. - /// An instantiated ISessionFactory. + /// An ISessionFactory if found, otherwise. [MethodImpl(MethodImplOptions.Synchronized)] public static ISessionFactory GetInstance(string uid) {