From 3bb99e9feba1af9b9bb7618b0065d472be61941a 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, 27 Dec 2020 19:18:53 +0100 Subject: [PATCH 1/3] Add test for #2627 --- .../Async/NHSpecificTest/GH2627/Fixture.cs | 69 +++++++++++++++++++ .../NHSpecificTest/GH2627/Child.cs | 11 +++ .../NHSpecificTest/GH2627/Entity.cs | 10 +++ .../NHSpecificTest/GH2627/Fixture.cs | 57 +++++++++++++++ .../NHSpecificTest/GH2627/Mappings.hbm.xml | 16 +++++ 5 files changed, 163 insertions(+) create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/GH2627/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2627/Child.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2627/Entity.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2627/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2627/Mappings.hbm.xml diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2627/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2627/Fixture.cs new file mode 100644 index 00000000000..ac24f9f72c5 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2627/Fixture.cs @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.Linq; +using NUnit.Framework; +using NHibernate.Linq; + +namespace NHibernate.Test.NHSpecificTest.GH2627 +{ + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var e1 = new Entity {Name = "Bob"}; + session.Save(e1); + + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.CreateQuery("delete from Child").ExecuteUpdate(); + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + + transaction.Commit(); + } + } + + [Test] + public async Task NullRefInMergeAsync() + { + Child child; + using (var session = OpenSession()) + using (var t = session.BeginTransaction()) + { + child = new Child + { + Parent = await (session.Query().FirstOrDefaultAsync()), + Name = "John" + }; + + await (t.CommitAsync()); + } + + using (var session = OpenSession()) + using (var t = session.BeginTransaction()) + { + await (session.MergeAsync(child)); + await (t.CommitAsync()); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2627/Child.cs b/src/NHibernate.Test/NHSpecificTest/GH2627/Child.cs new file mode 100644 index 00000000000..55ac0288207 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2627/Child.cs @@ -0,0 +1,11 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH2627 +{ + public class Child + { + public virtual Guid Id { get; set; } + public virtual Entity Parent { get; set; } + public virtual string Name { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2627/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH2627/Entity.cs new file mode 100644 index 00000000000..8757939fdf4 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2627/Entity.cs @@ -0,0 +1,10 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH2627 +{ + public class Entity + { + public virtual Guid Id { get; set; } + public virtual string Name { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2627/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2627/Fixture.cs new file mode 100644 index 00000000000..44f36d3a503 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2627/Fixture.cs @@ -0,0 +1,57 @@ +using System.Linq; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH2627 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var e1 = new Entity {Name = "Bob"}; + session.Save(e1); + + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.CreateQuery("delete from Child").ExecuteUpdate(); + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + + transaction.Commit(); + } + } + + [Test] + public void NullRefInMerge() + { + Child child; + using (var session = OpenSession()) + using (var t = session.BeginTransaction()) + { + child = new Child + { + Parent = session.Query().FirstOrDefault(), + Name = "John" + }; + + t.Commit(); + } + + using (var session = OpenSession()) + using (var t = session.BeginTransaction()) + { + session.Merge(child); + t.Commit(); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2627/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH2627/Mappings.hbm.xml new file mode 100644 index 00000000000..6c4296a93e9 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2627/Mappings.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + From 972b856861051f67255a352e1a5b24accde0fdfa 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, 31 Jan 2021 21:56:20 +0100 Subject: [PATCH 2/3] Fix #2627 --- src/NHibernate/Async/Type/ManyToOneType.cs | 1 - .../XmlHbmBinding/ClassCompositeIdBinder.cs | 2 +- .../Cfg/XmlHbmBinding/CollectionBinder.cs | 6 +-- .../Cfg/XmlHbmBinding/PropertiesBinder.cs | 4 +- src/NHibernate/Mapping/ManyToOne.cs | 22 ++++++++-- src/NHibernate/Mapping/OneToMany.cs | 4 +- src/NHibernate/Type/ManyToOneType.cs | 42 +++++++++++++++---- src/NHibernate/Type/TypeFactory.cs | 27 ++++++++++-- 8 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/NHibernate/Async/Type/ManyToOneType.cs b/src/NHibernate/Async/Type/ManyToOneType.cs index e288555bddb..d341713e944 100644 --- a/src/NHibernate/Async/Type/ManyToOneType.cs +++ b/src/NHibernate/Async/Type/ManyToOneType.cs @@ -11,7 +11,6 @@ using System; using System.Data.Common; using NHibernate.Engine; -using NHibernate.Persister.Entity; using NHibernate.SqlTypes; using NHibernate.Util; diff --git a/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs b/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs index 00e384c5e76..cd22cf6d6fb 100644 --- a/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs +++ b/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs @@ -101,7 +101,7 @@ private void BindComponent(System.Type reflectedClass, string path, HbmComposite if (keyManyToOneSchema != null) { - var manyToOne = new ManyToOne(compositeId.Table); + var manyToOne = new ManyToOne(compositeId.Table, compositeId.Owner); string propertyName = keyManyToOneSchema.name == null ? null : StringHelper.Qualify(path, keyManyToOneSchema.name); diff --git a/src/NHibernate/Cfg/XmlHbmBinding/CollectionBinder.cs b/src/NHibernate/Cfg/XmlHbmBinding/CollectionBinder.cs index 8984459d119..d82bca86d76 100644 --- a/src/NHibernate/Cfg/XmlHbmBinding/CollectionBinder.cs +++ b/src/NHibernate/Cfg/XmlHbmBinding/CollectionBinder.cs @@ -655,13 +655,13 @@ private void BindMapSecondPass(HbmMap mapMapping, Map model, } else if ((indexManyToManyMapping = mapMapping.Item as HbmIndexManyToMany) != null) { - var manyToOne = new ManyToOne(model.CollectionTable); + var manyToOne = new ManyToOne(model.CollectionTable, model.Owner); BindIndexManyToMany(indexManyToManyMapping, manyToOne, IndexedCollection.DefaultIndexColumnName, model.IsOneToMany); model.Index = manyToOne; } else if ((mapKeyManyToManyMapping = mapMapping.Item as HbmMapKeyManyToMany) != null) { - var manyToOne = new ManyToOne(model.CollectionTable); + var manyToOne = new ManyToOne(model.CollectionTable, model.Owner); BindMapKeyManyToMany(mapKeyManyToManyMapping, manyToOne, IndexedCollection.DefaultIndexColumnName, model.IsOneToMany); model.Index = manyToOne; } @@ -813,7 +813,7 @@ private void BindCollectionSecondPass(ICollectionPropertiesMapping collectionMap private void BindManyToMany(HbmManyToMany manyToManyMapping, Mapping.Collection model) { - var manyToMany = new ManyToOne(model.CollectionTable); + var manyToMany = new ManyToOne(model.CollectionTable, model.Owner); model.Element = manyToMany; new ValuePropertyBinder(manyToMany, Mappings).BindSimpleValue(manyToManyMapping, Mapping.Collection.DefaultElementColumnName, false); diff --git a/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs b/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs index 3fbbc63aa57..706cb9e81c1 100644 --- a/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs +++ b/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs @@ -129,7 +129,7 @@ public void Bind(IEnumerable properties, Table table, ID } else if ((manyToOneMapping = entityPropertyMapping as HbmManyToOne) != null) { - var value = new ManyToOne(table); + var value = new ManyToOne(table, persistentClass); BindManyToOne(manyToOneMapping, value, propertyName, true); property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas); BindManyToOneProperty(manyToOneMapping, property); @@ -189,7 +189,7 @@ public void Bind(IEnumerable properties, Table table, ID } else if ((keyManyToOneMapping = entityPropertyMapping as HbmKeyManyToOne) != null) { - var value = new ManyToOne(table); + var value = new ManyToOne(table, persistentClass); BindKeyManyToOne(keyManyToOneMapping, value, propertyName, componetDefaultNullable); property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas); } diff --git a/src/NHibernate/Mapping/ManyToOne.cs b/src/NHibernate/Mapping/ManyToOne.cs index 7500f7f7997..711c14247ca 100644 --- a/src/NHibernate/Mapping/ManyToOne.cs +++ b/src/NHibernate/Mapping/ManyToOne.cs @@ -9,11 +9,24 @@ namespace NHibernate.Mapping [Serializable] public class ManyToOne : ToOne { + private readonly string _entityName; + + /// + /// Construct a many-to-one mapping. + /// + /// The table. + /// The association owner. + public ManyToOne(Table table, PersistentClass owner) : base(table) + { + _entityName = owner?.EntityName; + } + /// /// /// /// - public ManyToOne(Table table) : base(table) + [Obsolete("Use overload with owner instead.")] + public ManyToOne(Table table) : this(table, null) { } @@ -41,7 +54,7 @@ public bool IsLogicalOneToOne get { return isLogicalOneToOne; } set { isLogicalOneToOne = value; } } - + public string PropertyName { get; set; } private IType type; @@ -51,8 +64,9 @@ public override IType Type { if (type == null) { - type = - TypeFactory.ManyToOne(ReferencedEntityName, ReferencedPropertyName, IsLazy, UnwrapProxy, IsIgnoreNotFound, isLogicalOneToOne, PropertyName); + type = TypeFactory.ManyToOne( + ReferencedEntityName, ReferencedPropertyName, IsLazy, UnwrapProxy, + IsIgnoreNotFound, isLogicalOneToOne, _entityName, PropertyName); } return type; } diff --git a/src/NHibernate/Mapping/OneToMany.cs b/src/NHibernate/Mapping/OneToMany.cs index b349e4df98b..0d92d564d66 100644 --- a/src/NHibernate/Mapping/OneToMany.cs +++ b/src/NHibernate/Mapping/OneToMany.cs @@ -23,7 +23,9 @@ public OneToMany(PersistentClass owner) private EntityType EntityType { - get { return TypeFactory.ManyToOne(ReferencedEntityName, null, false, false, IsIgnoreNotFound, false, null); } + get { return TypeFactory.ManyToOne( + ReferencedEntityName, null, false, false, + IsIgnoreNotFound, false, null, null); } } public bool IsIgnoreNotFound diff --git a/src/NHibernate/Type/ManyToOneType.cs b/src/NHibernate/Type/ManyToOneType.cs index b38fb35eba8..00b54cdaf0c 100644 --- a/src/NHibernate/Type/ManyToOneType.cs +++ b/src/NHibernate/Type/ManyToOneType.cs @@ -1,7 +1,6 @@ using System; using System.Data.Common; using NHibernate.Engine; -using NHibernate.Persister.Entity; using NHibernate.SqlTypes; using NHibernate.Util; @@ -15,6 +14,7 @@ public partial class ManyToOneType : EntityType { private readonly bool ignoreNotFound; private readonly bool isLogicalOneToOne; + private readonly string _entityName; public ManyToOneType(string className) : this(className, false) @@ -30,18 +30,41 @@ public ManyToOneType(string className, bool lazy) } //Since 5.3 - [Obsolete("Use Constructor with property name")] + [Obsolete("Use Constructor with owner property name and property name")] public ManyToOneType(string entityName, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, bool ignoreNotFound, bool isLogicalOneToOne) - : this(entityName, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null) + : this(entityName, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null, null) { } + //Since 5.3.6 + [Obsolete("Use Constructor with owner entity name")] public ManyToOneType(string entityName, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, bool ignoreNotFound, bool isLogicalOneToOne, string propertyName) - : base(entityName, uniqueKeyPropertyName, !lazy, unwrapProxy) + : this(entityName, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null, propertyName) + { + } + + /// + /// Build a many-to-one relation. + /// + /// The referenced entity name. + /// The property-ref name, or if we reference + /// the PK of the associated entity. + /// Is the association lazily loaded? + /// Is unwrapping of proxies allowed for this association; unwrapping says to return + /// the "implementation target" of lazy proxies; typically only possible with lazy="no-proxy". + /// for throwing an exception if the referenced + /// entity is missing in the database, for yielding instead. + /// + /// The property owner entity name. + /// The property name. + public ManyToOneType(string referencedEntityName, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, + bool ignoreNotFound, bool isLogicalOneToOne, string entityName, string propertyName) + : base(referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy) { this.ignoreNotFound = ignoreNotFound; this.isLogicalOneToOne = isLogicalOneToOne; PropertyName = propertyName; + _entityName = entityName; } public override int GetColumnSpan(IMapping mapping) @@ -162,11 +185,16 @@ private bool IsIdentifier(object value, ISessionImplementor session) public override bool IsNull(object owner, ISessionImplementor session) { - if (IsNullable && !string.IsNullOrEmpty(PropertyName)) + if (IsNullable && !string.IsNullOrEmpty(PropertyName) && owner != null) { - EntityEntry entry = session.PersistenceContext.GetEntry(owner); + var ownerPersister = session.Factory.GetEntityPersister(_entityName); + var id = session.GetContextEntityIdentifier(owner); + if (id == null) + return false; + + var entityKey = session.GenerateEntityKey(id, ownerPersister); - return session.PersistenceContext.IsPropertyNull(entry.EntityKey, PropertyName); + return session.PersistenceContext.IsPropertyNull(entityKey, PropertyName); } return false; diff --git a/src/NHibernate/Type/TypeFactory.cs b/src/NHibernate/Type/TypeFactory.cs index 271b85ff4ea..eb33be5997a 100644 --- a/src/NHibernate/Type/TypeFactory.cs +++ b/src/NHibernate/Type/TypeFactory.cs @@ -957,18 +957,39 @@ public static EntityType ManyToOne(string persistentClass, bool lazy) /// /// A many-to-one association type for the given class and cascade style. /// + /// The referenced class. + /// The property-ref name, or if we reference + /// the PK of the associated entity. + /// Is the association lazily loaded? + /// Is unwrapping of proxies allowed for this association; unwrapping says to return + /// the "implementation target" of lazy proxies; typically only possible with lazy="no-proxy". + /// for throwing an exception if the referenced + /// entity is missing in the database, for yielding instead. + /// + /// The property owner entity name. + /// The property name. + public static EntityType ManyToOne(string persistentClass, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, + bool ignoreNotFound, bool isLogicalOneToOne, string entityName, string propertyName) + { + return new ManyToOneType(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, entityName, propertyName); + } + + /// + /// A many-to-one association type for the given class and cascade style. + /// + [Obsolete("Use ManyToOne with owner entity name")] public static EntityType ManyToOne(string persistentClass, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, bool ignoreNotFound, bool isLogicalOneToOne, string propertyName) { - return new ManyToOneType(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, propertyName); + return ManyToOne(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null, propertyName); } /// /// A many-to-one association type for the given class and cascade style. /// - [Obsolete("Use ManyToOne with propertyName")] + [Obsolete("Use ManyToOne with owner entity name and propertyName")] public static EntityType ManyToOne(string persistentClass, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, bool ignoreNotFound, bool isLogicalOneToOne) { - return ManyToOne(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null); + return ManyToOne(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null, null); } public static CollectionType Array(string role, string propertyRef, System.Type elementClass) From 2f6c555e5e511310759f14ff93e408ca9c93f222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:52:34 +0100 Subject: [PATCH 3/3] Revert and do a simpler fix Co-authored-by: Roman Artiukhin --- src/NHibernate/Async/Type/ManyToOneType.cs | 1 + .../XmlHbmBinding/ClassCompositeIdBinder.cs | 2 +- .../Cfg/XmlHbmBinding/CollectionBinder.cs | 6 +- .../Cfg/XmlHbmBinding/PropertiesBinder.cs | 4 +- src/NHibernate/Mapping/ManyToOne.cs | 22 ++------ src/NHibernate/Mapping/OneToMany.cs | 4 +- src/NHibernate/Type/ManyToOneType.cs | 56 +++++++------------ src/NHibernate/Type/TypeFactory.cs | 27 +-------- 8 files changed, 34 insertions(+), 88 deletions(-) diff --git a/src/NHibernate/Async/Type/ManyToOneType.cs b/src/NHibernate/Async/Type/ManyToOneType.cs index d341713e944..d5615a85d7a 100644 --- a/src/NHibernate/Async/Type/ManyToOneType.cs +++ b/src/NHibernate/Async/Type/ManyToOneType.cs @@ -11,6 +11,7 @@ using System; using System.Data.Common; using NHibernate.Engine; +using NHibernate.Proxy; using NHibernate.SqlTypes; using NHibernate.Util; diff --git a/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs b/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs index cd22cf6d6fb..00e384c5e76 100644 --- a/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs +++ b/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs @@ -101,7 +101,7 @@ private void BindComponent(System.Type reflectedClass, string path, HbmComposite if (keyManyToOneSchema != null) { - var manyToOne = new ManyToOne(compositeId.Table, compositeId.Owner); + var manyToOne = new ManyToOne(compositeId.Table); string propertyName = keyManyToOneSchema.name == null ? null : StringHelper.Qualify(path, keyManyToOneSchema.name); diff --git a/src/NHibernate/Cfg/XmlHbmBinding/CollectionBinder.cs b/src/NHibernate/Cfg/XmlHbmBinding/CollectionBinder.cs index d82bca86d76..8984459d119 100644 --- a/src/NHibernate/Cfg/XmlHbmBinding/CollectionBinder.cs +++ b/src/NHibernate/Cfg/XmlHbmBinding/CollectionBinder.cs @@ -655,13 +655,13 @@ private void BindMapSecondPass(HbmMap mapMapping, Map model, } else if ((indexManyToManyMapping = mapMapping.Item as HbmIndexManyToMany) != null) { - var manyToOne = new ManyToOne(model.CollectionTable, model.Owner); + var manyToOne = new ManyToOne(model.CollectionTable); BindIndexManyToMany(indexManyToManyMapping, manyToOne, IndexedCollection.DefaultIndexColumnName, model.IsOneToMany); model.Index = manyToOne; } else if ((mapKeyManyToManyMapping = mapMapping.Item as HbmMapKeyManyToMany) != null) { - var manyToOne = new ManyToOne(model.CollectionTable, model.Owner); + var manyToOne = new ManyToOne(model.CollectionTable); BindMapKeyManyToMany(mapKeyManyToManyMapping, manyToOne, IndexedCollection.DefaultIndexColumnName, model.IsOneToMany); model.Index = manyToOne; } @@ -813,7 +813,7 @@ private void BindCollectionSecondPass(ICollectionPropertiesMapping collectionMap private void BindManyToMany(HbmManyToMany manyToManyMapping, Mapping.Collection model) { - var manyToMany = new ManyToOne(model.CollectionTable, model.Owner); + var manyToMany = new ManyToOne(model.CollectionTable); model.Element = manyToMany; new ValuePropertyBinder(manyToMany, Mappings).BindSimpleValue(manyToManyMapping, Mapping.Collection.DefaultElementColumnName, false); diff --git a/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs b/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs index 706cb9e81c1..3fbbc63aa57 100644 --- a/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs +++ b/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs @@ -129,7 +129,7 @@ public void Bind(IEnumerable properties, Table table, ID } else if ((manyToOneMapping = entityPropertyMapping as HbmManyToOne) != null) { - var value = new ManyToOne(table, persistentClass); + var value = new ManyToOne(table); BindManyToOne(manyToOneMapping, value, propertyName, true); property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas); BindManyToOneProperty(manyToOneMapping, property); @@ -189,7 +189,7 @@ public void Bind(IEnumerable properties, Table table, ID } else if ((keyManyToOneMapping = entityPropertyMapping as HbmKeyManyToOne) != null) { - var value = new ManyToOne(table, persistentClass); + var value = new ManyToOne(table); BindKeyManyToOne(keyManyToOneMapping, value, propertyName, componetDefaultNullable); property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas); } diff --git a/src/NHibernate/Mapping/ManyToOne.cs b/src/NHibernate/Mapping/ManyToOne.cs index 711c14247ca..7500f7f7997 100644 --- a/src/NHibernate/Mapping/ManyToOne.cs +++ b/src/NHibernate/Mapping/ManyToOne.cs @@ -9,24 +9,11 @@ namespace NHibernate.Mapping [Serializable] public class ManyToOne : ToOne { - private readonly string _entityName; - - /// - /// Construct a many-to-one mapping. - /// - /// The table. - /// The association owner. - public ManyToOne(Table table, PersistentClass owner) : base(table) - { - _entityName = owner?.EntityName; - } - /// /// /// /// - [Obsolete("Use overload with owner instead.")] - public ManyToOne(Table table) : this(table, null) + public ManyToOne(Table table) : base(table) { } @@ -54,7 +41,7 @@ public bool IsLogicalOneToOne get { return isLogicalOneToOne; } set { isLogicalOneToOne = value; } } - + public string PropertyName { get; set; } private IType type; @@ -64,9 +51,8 @@ public override IType Type { if (type == null) { - type = TypeFactory.ManyToOne( - ReferencedEntityName, ReferencedPropertyName, IsLazy, UnwrapProxy, - IsIgnoreNotFound, isLogicalOneToOne, _entityName, PropertyName); + type = + TypeFactory.ManyToOne(ReferencedEntityName, ReferencedPropertyName, IsLazy, UnwrapProxy, IsIgnoreNotFound, isLogicalOneToOne, PropertyName); } return type; } diff --git a/src/NHibernate/Mapping/OneToMany.cs b/src/NHibernate/Mapping/OneToMany.cs index 0d92d564d66..b349e4df98b 100644 --- a/src/NHibernate/Mapping/OneToMany.cs +++ b/src/NHibernate/Mapping/OneToMany.cs @@ -23,9 +23,7 @@ public OneToMany(PersistentClass owner) private EntityType EntityType { - get { return TypeFactory.ManyToOne( - ReferencedEntityName, null, false, false, - IsIgnoreNotFound, false, null, null); } + get { return TypeFactory.ManyToOne(ReferencedEntityName, null, false, false, IsIgnoreNotFound, false, null); } } public bool IsIgnoreNotFound diff --git a/src/NHibernate/Type/ManyToOneType.cs b/src/NHibernate/Type/ManyToOneType.cs index 00b54cdaf0c..172bc8f1b99 100644 --- a/src/NHibernate/Type/ManyToOneType.cs +++ b/src/NHibernate/Type/ManyToOneType.cs @@ -1,6 +1,7 @@ using System; using System.Data.Common; using NHibernate.Engine; +using NHibernate.Proxy; using NHibernate.SqlTypes; using NHibernate.Util; @@ -14,7 +15,6 @@ public partial class ManyToOneType : EntityType { private readonly bool ignoreNotFound; private readonly bool isLogicalOneToOne; - private readonly string _entityName; public ManyToOneType(string className) : this(className, false) @@ -30,41 +30,18 @@ public ManyToOneType(string className, bool lazy) } //Since 5.3 - [Obsolete("Use Constructor with owner property name and property name")] + [Obsolete("Use Constructor with property name")] public ManyToOneType(string entityName, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, bool ignoreNotFound, bool isLogicalOneToOne) - : this(entityName, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null, null) + : this(entityName, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null) { } - //Since 5.3.6 - [Obsolete("Use Constructor with owner entity name")] public ManyToOneType(string entityName, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, bool ignoreNotFound, bool isLogicalOneToOne, string propertyName) - : this(entityName, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null, propertyName) - { - } - - /// - /// Build a many-to-one relation. - /// - /// The referenced entity name. - /// The property-ref name, or if we reference - /// the PK of the associated entity. - /// Is the association lazily loaded? - /// Is unwrapping of proxies allowed for this association; unwrapping says to return - /// the "implementation target" of lazy proxies; typically only possible with lazy="no-proxy". - /// for throwing an exception if the referenced - /// entity is missing in the database, for yielding instead. - /// - /// The property owner entity name. - /// The property name. - public ManyToOneType(string referencedEntityName, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, - bool ignoreNotFound, bool isLogicalOneToOne, string entityName, string propertyName) - : base(referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy) + : base(entityName, uniqueKeyPropertyName, !lazy, unwrapProxy) { this.ignoreNotFound = ignoreNotFound; this.isLogicalOneToOne = isLogicalOneToOne; PropertyName = propertyName; - _entityName = entityName; } public override int GetColumnSpan(IMapping mapping) @@ -185,19 +162,24 @@ private bool IsIdentifier(object value, ISessionImplementor session) public override bool IsNull(object owner, ISessionImplementor session) { - if (IsNullable && !string.IsNullOrEmpty(PropertyName) && owner != null) - { - var ownerPersister = session.Factory.GetEntityPersister(_entityName); - var id = session.GetContextEntityIdentifier(owner); - if (id == null) - return false; + if (!IsNullable || string.IsNullOrEmpty(PropertyName) || owner == null) + return false; - var entityKey = session.GenerateEntityKey(id, ownerPersister); + var entityKey = GetEntityKey(owner, session); + if (entityKey == null) + return false; - return session.PersistenceContext.IsPropertyNull(entityKey, PropertyName); - } + return session.PersistenceContext.IsPropertyNull(entityKey, PropertyName); + } - return false; + private static EntityKey GetEntityKey(object owner, ISessionImplementor session) + { + var entry = session.PersistenceContext.GetEntry(owner); + if (entry != null) + return entry.EntityKey; + if (owner is INHibernateProxy proxy) + return session.GenerateEntityKey(proxy.HibernateLazyInitializer.Identifier, session.Factory.GetEntityPersister(proxy.HibernateLazyInitializer.EntityName)); + return null; } public override object Disassemble(object value, ISessionImplementor session, object owner) diff --git a/src/NHibernate/Type/TypeFactory.cs b/src/NHibernate/Type/TypeFactory.cs index eb33be5997a..271b85ff4ea 100644 --- a/src/NHibernate/Type/TypeFactory.cs +++ b/src/NHibernate/Type/TypeFactory.cs @@ -957,39 +957,18 @@ public static EntityType ManyToOne(string persistentClass, bool lazy) /// /// A many-to-one association type for the given class and cascade style. /// - /// The referenced class. - /// The property-ref name, or if we reference - /// the PK of the associated entity. - /// Is the association lazily loaded? - /// Is unwrapping of proxies allowed for this association; unwrapping says to return - /// the "implementation target" of lazy proxies; typically only possible with lazy="no-proxy". - /// for throwing an exception if the referenced - /// entity is missing in the database, for yielding instead. - /// - /// The property owner entity name. - /// The property name. - public static EntityType ManyToOne(string persistentClass, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, - bool ignoreNotFound, bool isLogicalOneToOne, string entityName, string propertyName) - { - return new ManyToOneType(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, entityName, propertyName); - } - - /// - /// A many-to-one association type for the given class and cascade style. - /// - [Obsolete("Use ManyToOne with owner entity name")] public static EntityType ManyToOne(string persistentClass, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, bool ignoreNotFound, bool isLogicalOneToOne, string propertyName) { - return ManyToOne(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null, propertyName); + return new ManyToOneType(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, propertyName); } /// /// A many-to-one association type for the given class and cascade style. /// - [Obsolete("Use ManyToOne with owner entity name and propertyName")] + [Obsolete("Use ManyToOne with propertyName")] public static EntityType ManyToOne(string persistentClass, string uniqueKeyPropertyName, bool lazy, bool unwrapProxy, bool ignoreNotFound, bool isLogicalOneToOne) { - return ManyToOne(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null, null); + return ManyToOne(persistentClass, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne, null); } public static CollectionType Array(string role, string propertyRef, System.Type elementClass)