Skip to content

WIP - Better fix for inverse collection forgetting changes #2047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/AsyncGenerator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
- conversion: Ignore
name: InitializeLazyPropertiesFromCache
containingTypeName: AbstractEntityPersister
- conversion: Ignore
name: GetElementOwnerPropertyValue
containingTypeName: AbstractCollectionPersister
- conversion: Ignore
name: Invoke
containingTypeName: BasicLazyInitializer
Expand Down
217 changes: 217 additions & 0 deletions src/NHibernate.Test/Async/CollectionCompositeKey/Fixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using NHibernate.Event;
using NHibernate.Persister.Collection;
using NUnit.Framework;
using NUnit.Framework.Constraints;

namespace NHibernate.Test.CollectionCompositeKey
{
using System.Threading.Tasks;
using System.Threading;
[TestFixture]
public class FixtureAsync : TestCase
{
private readonly Parent _parentId = new Parent("1", 1);
private int _currentChildId = 1;
private int _currentGrandChildId = 1;

protected override string[] Mappings => new [] { "CollectionCompositeKey.CollectionOwner.hbm.xml" };

protected override string MappingsAssembly => "NHibernate.Test";

protected override string CacheConcurrencyStrategy => null;

protected override void OnSetUp()
{
using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
var parent = CreateParent(_parentId.Code, _parentId.Number);

s.Save(parent);
t.Commit();
}
}

protected override void OnTearDown()
{
using (var session = OpenSession())
using (var tx = session.BeginTransaction())
{
session.Delete("from GrandChild");
session.Delete("from Child");
session.Delete("from Parent");
tx.Commit();
}
base.OnTearDown();
}

[TestCase(nameof(Parent.Children), true, true)]
[TestCase(nameof(Parent.ChildrenByForeignKeys), true, false)]
[TestCase(nameof(Parent.ChildrenByComponent), true, true)]
[TestCase(nameof(Parent.ChildrenByComponentForeignKeys), true, false)]
[TestCase(nameof(Parent.ChildrenNoProperties), false, false)]
[TestCase(nameof(Parent.ChildrenByUniqueKey), true, true)]
[TestCase(nameof(Parent.ChildrenByUniqueKeyNoManyToOne), true, false)]
[TestCase(nameof(Parent.ChildrenByCompositeUniqueKey), true, true)]
[TestCase(nameof(Parent.ChildrenByCompositeUniqueKeyNoManyToOne), true, false)]
public Task TestGetElementOwnerForChildCollectionsAsync(string collectionProperty, bool propertyExists, bool hasManyToOne, CancellationToken cancellationToken = default(CancellationToken))
{
return TestGetElementOwnerForChildCollectionsAsync<Child>(collectionProperty, propertyExists, hasManyToOne, (parent, child) => child.Id, cancellationToken);
}

[TestCase(nameof(Parent.GrandChildren), true, true)]
public Task TestGetElementOwnerForGrandChildCollectionsAsync(string collectionProperty, bool propertyExists, bool hasManyToOne, CancellationToken cancellationToken = default(CancellationToken))
{
return TestGetElementOwnerForChildCollectionsAsync<GrandChild>(collectionProperty, propertyExists, hasManyToOne, GetGrandChildId, cancellationToken);
}

private static object GetGrandChildId(Parent parent, GrandChild child)
{
if (parent == null)
{
return null; // Not supported
}

child.GrandParent = parent;
return child;
}

private async Task TestGetElementOwnerForChildCollectionsAsync<TChild>(string collectionProperty, bool propertyExists, bool hasManyToOne, Func<Parent, TChild, object> getChildId, CancellationToken cancellationToken = default(CancellationToken))
where TChild : class
{
var persister = Sfi.GetEntityPersister(typeof(Parent).FullName);
var collPersister = GetCollectionPersister(collectionProperty);
TChild firstChild = null;

var propertyIndex = -1;
for (var i = 0; i < persister.PropertyNames.Length; i++)
{
if (persister.PropertyNames[i] == collectionProperty)
{
propertyIndex = i;
break;
}
}

Assert.That(propertyIndex, Is.Not.EqualTo(-1));

// Test when collection is loaded
using (var s = (IEventSource) OpenSession())
using (var tx = s.BeginTransaction())
{
var parent = await (s.GetAsync<Parent>(_parentId, cancellationToken));
Assert.That(parent, Is.Not.Null);

var collection = (IList<TChild>) persister.GetPropertyValue(parent, propertyIndex);
foreach (var child in collection)
{
if (firstChild == null)
{
firstChild = child;
}

Assert.That(collPersister.GetElementOwner(child, s), propertyExists ? Is.EqualTo(parent) : (IResolveConstraint) Is.Null);
}

await (tx.CommitAsync(cancellationToken));
}

Assert.That(firstChild, Is.Not.Null);

// Test when collection is not loaded
using (var s = (IEventSource) OpenSession())
using (var tx = s.BeginTransaction())
{
var parent = await (s.GetAsync<Parent>(_parentId, cancellationToken));
var child = await (s.GetAsync<TChild>(getChildId(parent, firstChild), cancellationToken));
Assert.That(parent, Is.Not.Null);
Assert.That(child, Is.Not.Null);

Assert.That(collPersister.GetElementOwner(child, s), propertyExists ? Is.EqualTo(parent) : (IResolveConstraint) Is.Null);

await (tx.CommitAsync(cancellationToken));
}

// Test when only the child is loaded
using (var s = (IEventSource) OpenSession())
using (var tx = s.BeginTransaction())
{
var id = getChildId(null, firstChild);
if (id != null)
{
var child = await (s.GetAsync<TChild>(id, cancellationToken));
Assert.That(child, Is.Not.Null);

Assert.That(collPersister.GetElementOwner(child, s), hasManyToOne ? Is.InstanceOf<Parent>() : (IResolveConstraint) Is.Null);
}

await (tx.CommitAsync(cancellationToken));
}

// Test transient
using (var s = (IEventSource) OpenSession())
using (var tx = s.BeginTransaction())
{
var parent = CreateParent("2", 2);
var collection = (IList<TChild>) persister.GetPropertyValue(parent, propertyIndex);

foreach (var child in collection)
{
Assert.That(collPersister.GetElementOwner(child, s), hasManyToOne ? Is.EqualTo(parent) : (IResolveConstraint) Is.Null);
}

await (tx.CommitAsync(cancellationToken));
}
}

private AbstractCollectionPersister GetCollectionPersister(string collectionProperty)
{
return (AbstractCollectionPersister) Sfi.GetCollectionPersister($"{typeof(Parent).FullName}.{collectionProperty}");
}

private Parent CreateParent(string code, int number)
{
var parent = new Parent(code, number)
{
Name = $"parent{number}",
ReferenceCode = code,
ReferenceNumber = number
};

parent.Children.Add(new Child(_currentChildId++, "child", parent) { Parent = parent });
parent.ChildrenByForeignKeys.Add(new Child(_currentChildId++, "childFk", parent) { ParentNumber = parent.Number, ParentCode = parent.Code });
parent.ChildrenByComponent.Add(new Child(_currentChildId++, "childCo", parent) { Component = new ChildComponent { Parent = parent } });
parent.ChildrenByComponentForeignKeys.Add(
new Child(_currentChildId++, "childCoFk", parent)
{
Component = new ChildComponent { ParentNumber = parent.Number, ParentCode = parent.Code }
});
parent.ChildrenNoProperties.Add(new Child(_currentChildId++, "childNp", parent));
parent.ChildrenByUniqueKey.Add(new Child(_currentChildId++, "childUk", parent) { ParentByName = parent });
parent.ChildrenByUniqueKeyNoManyToOne.Add(new Child(_currentChildId++, "childUkFk", parent) { ParentName = parent.Name });
parent.ChildrenByCompositeUniqueKey.Add(new Child(_currentChildId++, "childCoUk", parent) { ParentByReference = parent });
parent.ChildrenByCompositeUniqueKeyNoManyToOne.Add(
new Child(_currentChildId++, "childCoUkFk", parent)
{
ParentReferenceCode = parent.ReferenceCode,
ParentReferenceNumber = parent.ReferenceNumber
});

parent.GrandChildren.Add(new GrandChild(_currentGrandChildId, "grandChild", parent));

return parent;
}
}
}
Loading