Skip to content
128 changes: 128 additions & 0 deletions src/NHibernate.Test/Async/NHSpecificTest/GH1419/FixtureByCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//------------------------------------------------------------------------------
// <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.Linq;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH1419
{
using System.Threading.Tasks;
using System.Threading;
[TestFixture]
public class ByCodeFixtureAsync : TestCaseMappingByCode
{
private Guid ParentId;

protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.Class<EntityParent>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.Property(x => x.Name);
rc.ManyToOne(ep => ep.Child);
rc.ManyToOne(ep => ep.ChildAssigned);
});

mapper.Class<EntityChild>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.Property(x => x.Name);
});

mapper.Class<EntityChildAssigned>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.Assigned));
rc.Property(x => x.Name);
});

return mapper.CompileMappingForAllExplicitlyAddedEntities();
}

protected override void OnSetUp()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
var child = new EntityChild { Name = "InitialChild" };

var assigned = new EntityChildAssigned { Id = 1, Name = "InitialChild" };

var parent = new EntityParent
{
Name = "InitialParent",
Child = child,
ChildAssigned = assigned
};
session.Save(child);
session.Save(parent);
session.Save(assigned);

session.Flush();
transaction.Commit();
ParentId = parent.Id;
}
}

protected override void OnTearDown()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
session.Delete("from System.Object");

session.Flush();
transaction.Commit();
}
}

[Test]
public async Task SessionIsDirtyShouldNotFailForNewManyToOneObjectAsync()
{
using (var session = OpenSession())
using (session.BeginTransaction())
{
var parent = await (GetParentAsync(session));

//parent.Child entity is not cascaded, I want to save it explictilty later
parent.Child = new EntityChild { Name = "NewManyToOneChild" };

var isDirty = false;
Assert.That(async () => isDirty = await (session.IsDirtyAsync()), Throws.Nothing, "ISession.IsDirty() call should not fail for transient many-to-one object referenced in session.");
Assert.That(isDirty, "ISession.IsDirty() call should return true.");
}
}

[Test]
public async Task SessionIsDirtyShouldNotFailForNewManyToOneObjectWithAssignedIdAsync()
{
using (var session = OpenSession())
using (session.BeginTransaction())
{
var parent = await (GetParentAsync(session));

//parent.ChildAssigned entity is not cascaded, I want to save it explictilty later
parent.ChildAssigned = new EntityChildAssigned { Id = 2, Name = "NewManyToOneChildAssignedId" };

var isDirty = false;
Assert.That(async () => isDirty = await (session.IsDirtyAsync()), Throws.Nothing, "ISession.IsDirty() call should not fail for transient many-to-one object referenced in session.");
Assert.That(isDirty, "ISession.IsDirty() call should return true.");
}
}

private Task<EntityParent> GetParentAsync(ISession session, CancellationToken cancellationToken = default(CancellationToken))
{
return session.GetAsync<EntityParent>(ParentId, cancellationToken);
}
}
}
25 changes: 25 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH1419/Entities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;

namespace NHibernate.Test.NHSpecificTest.GH1419
{
public class EntityChild
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}

public class EntityParent
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual EntityChild Child { get; set; }
public virtual EntityChildAssigned ChildAssigned { get; set; }
}

public class EntityChildAssigned
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
}
116 changes: 116 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH1419/FixtureByCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Linq;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH1419
{
[TestFixture]
public class ByCodeFixture : TestCaseMappingByCode
{
private Guid ParentId;

protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.Class<EntityParent>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.Property(x => x.Name);
rc.ManyToOne(ep => ep.Child);
rc.ManyToOne(ep => ep.ChildAssigned);
});

mapper.Class<EntityChild>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.Property(x => x.Name);
});

mapper.Class<EntityChildAssigned>(rc =>
{
rc.Id(x => x.Id, m => m.Generator(Generators.Assigned));
rc.Property(x => x.Name);
});

return mapper.CompileMappingForAllExplicitlyAddedEntities();
}

protected override void OnSetUp()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
var child = new EntityChild { Name = "InitialChild" };

var assigned = new EntityChildAssigned { Id = 1, Name = "InitialChild" };

var parent = new EntityParent
{
Name = "InitialParent",
Child = child,
ChildAssigned = assigned
};
session.Save(child);
session.Save(parent);
session.Save(assigned);

session.Flush();
transaction.Commit();
ParentId = parent.Id;
}
}

protected override void OnTearDown()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
session.Delete("from System.Object");

session.Flush();
transaction.Commit();
}
}

[Test]
public void SessionIsDirtyShouldNotFailForNewManyToOneObject()
{
using (var session = OpenSession())
using (session.BeginTransaction())
{
var parent = GetParent(session);

//parent.Child entity is not cascaded, I want to save it explictilty later
parent.Child = new EntityChild { Name = "NewManyToOneChild" };

var isDirty = false;
Assert.That(() => isDirty = session.IsDirty(), Throws.Nothing, "ISession.IsDirty() call should not fail for transient many-to-one object referenced in session.");
Assert.That(isDirty, "ISession.IsDirty() call should return true.");
}
}

[Test]
public void SessionIsDirtyShouldNotFailForNewManyToOneObjectWithAssignedId()
{
using (var session = OpenSession())
using (session.BeginTransaction())
{
var parent = GetParent(session);

//parent.ChildAssigned entity is not cascaded, I want to save it explictilty later
parent.ChildAssigned = new EntityChildAssigned { Id = 2, Name = "NewManyToOneChildAssignedId" };

var isDirty = false;
Assert.That(() => isDirty = session.IsDirty(), Throws.Nothing, "ISession.IsDirty() call should not fail for transient many-to-one object referenced in session.");
Assert.That(isDirty, "ISession.IsDirty() call should return true.");
}
}

private EntityParent GetParent(ISession session)
{
return session.Get<EntityParent>(ParentId);
}
}
}
48 changes: 30 additions & 18 deletions src/NHibernate/Async/Type/ManyToOneType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,37 +135,49 @@ private Task<object> AssembleIdAsync(object oid, ISessionImplementor session, Ca
}
}

public override async Task<bool> IsDirtyAsync(object old, object current, ISessionImplementor session, CancellationToken cancellationToken)
public override Task<bool> IsDirtyAsync(object old, object current, ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (IsSame(old, current))
if (cancellationToken.IsCancellationRequested)
{
return false;
return Task.FromCanceled<bool>(cancellationToken);
}
return IsDirtyManyToOneAsync(old, current, null, session, cancellationToken);
}

object oldid = await (GetIdentifierAsync(old, session, cancellationToken)).ConfigureAwait(false);
object newid = await (GetIdentifierAsync(current, session, cancellationToken)).ConfigureAwait(false);
return await (GetIdentifierType(session).IsDirtyAsync(oldid, newid, session, cancellationToken)).ConfigureAwait(false);
public override Task<bool> IsDirtyAsync(object old, object current, bool[] checkable, ISessionImplementor session, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<bool>(cancellationToken);
}
return IsDirtyManyToOneAsync(old, current, IsAlwaysDirtyChecked ? null : checkable, session, cancellationToken);
}

public override async Task<bool> IsDirtyAsync(object old, object current, bool[] checkable, ISessionImplementor session, CancellationToken cancellationToken)
private async Task<bool> IsDirtyManyToOneAsync(object old, object current, bool[] checkable, ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (IsAlwaysDirtyChecked)
if (IsSame(old, current))
{
return await (IsDirtyAsync(old, current, session, cancellationToken)).ConfigureAwait(false);
return false;
}
else

if (old == null || current == null)
{
if (IsSame(old, current))
{
return false;
}
return true;
}

object oldid = await (GetIdentifierAsync(old, session, cancellationToken)).ConfigureAwait(false);
object newid = await (GetIdentifierAsync(current, session, cancellationToken)).ConfigureAwait(false);
return await (GetIdentifierType(session).IsDirtyAsync(oldid, newid, checkable, session, cancellationToken)).ConfigureAwait(false);
if ((await (ForeignKeys.IsTransientFastAsync(GetAssociatedEntityName(), current, session, cancellationToken)).ConfigureAwait(false)).GetValueOrDefault())
{
return true;
}

object oldid = await (GetIdentifierAsync(old, session, cancellationToken)).ConfigureAwait(false);
object newid = await (GetIdentifierAsync(current, session, cancellationToken)).ConfigureAwait(false);
IType identifierType = GetIdentifierType(session);

return checkable == null
? await (identifierType.IsDirtyAsync(oldid, newid, session, cancellationToken)).ConfigureAwait(false)
: await (identifierType.IsDirtyAsync(oldid, newid, checkable, session, cancellationToken)).ConfigureAwait(false);
}
}
}
Loading