-
Notifications
You must be signed in to change notification settings - Fork 934
Child object temporary added, then, removed, but gets inserted anyway #2028
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
Comments
The default session flush mode is By the way, you have reached the issue tracker, meant for bug reports or feature requests. Please post on the nhusers group for support requests (or see Groups). |
The |
I was able to reproduce the issue by adding the following test into
and altering the The issue here is that the existance check uses the element key and also the collection key: @MrGroovy Can you provide how your |
Just the Childs mapping: Set(
o => o.Childs,
m =>
{
m.Cascade(Cascade.All | Cascade.DeleteOrphans);
m.Key(mm =>
{
mm.Column("parent_id");
});
},
m => m.OneToMany()); Entire program: Click to open
using NHibernate;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
using System.Collections.Generic;
namespace NhOrphans
{
public class Program
{
public static void Main(string[] args)
{
var config = new NHibernate.Cfg.Configuration();
config.SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName);
config.SetProperty(NHibernate.Cfg.Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName);
config.SetProperty(NHibernate.Cfg.Environment.ReleaseConnections, "on_close");
//config.SetProperty(NHibernate.Cfg.Environment.ConnectionString, "data source=:memory:;DateTimeKind=Utc;");
config.SetProperty(NHibernate.Cfg.Environment.ConnectionString, "data source=Test.db3;DateTimeKind=Utc;");
config.SetProperty(NHibernate.Cfg.Environment.FormatSql, true.ToString());
config.SetProperty(NHibernate.Cfg.Environment.ShowSql, false.ToString());
ModelMapper mapper = new ModelMapper();
mapper.AddMapping(new ParentMapping());
mapper.AddMapping(new ChildMapping());
mapper.AddMapping(new SettingsMapping());
config.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
ISessionFactory sessionFactory = config.BuildSessionFactory();
ISession session = sessionFactory.OpenSession();
session.FlushMode = FlushMode.Auto;
//var schemaExport = new SchemaExport(config);
//schemaExport.Execute(false, true, false, session.Connection, null);
using (var transaction = session.BeginTransaction())
{
Parent parent = session.Get<Parent>("1");
var child = new Child();
child.Id = "99";
child.ChildName = "Child 99";
parent.Childs.Add(child);
// Auto flush? Inserts the child record?
Settings settings = session
.QueryOver<Settings>()
.Take(1)
.SingleOrDefault();
// Remove child from parent, child is no longer referenced
parent.Childs.Remove(child);
// Child record is not deleted here,
// parent_id column will be NULL.
session.Update(parent);
transaction.Commit();
}
}
}
public class Parent
{
public virtual string Id { get; set; }
public virtual string ParentName { get; set; }
public virtual ISet<Child> Childs { get; set; }
public Parent()
{
Childs = new HashSet<Child>();
}
}
public class Child
{
public virtual string Id { get; set; }
public virtual string ChildName { get; set; }
}
public class Settings
{
public virtual string Id { get; set; }
public virtual int SomeSetting { get; set; }
}
public class ParentMapping : ClassMapping<Parent>
{
public ParentMapping()
{
Table("parent");
Id(o => o.Id, m =>
{
m.Column("id");
m.Generator(Generators.Assigned);
});
Property(o => o.ParentName, m => m.Column("name"));
Set(
o => o.Childs,
m =>
{
m.Cascade(Cascade.All | Cascade.DeleteOrphans);
m.Key(mm =>
{
mm.Column("parent_id");
});
},
m => m.OneToMany());
}
}
public class ChildMapping : ClassMapping<Child>
{
public ChildMapping()
{
Table("child");
Id(o => o.Id, m =>
{
m.Column("id");
m.Generator(Generators.Assigned);
});
Property(o => o.ChildName, m => m.Column("name"));
}
}
public class SettingsMapping : ClassMapping<Settings>
{
public SettingsMapping()
{
Table("settings");
Id(o => o.Id, m =>
{
m.Column("id");
m.Generator(Generators.Assigned);
});
Property(o => o.SomeSetting, m => m.Column("some_setting"));
}
}
} |
I see, your example hits another issue that has to do with the auto flushing which adds the insert of the child into the action queue which is executed later when updating the parent. Here possible solutions are:
None of those two are easy to implement as far as I looked. |
In a simple parent -> child relationship when adding a child to a existing parent, then doing a query, then removing the child, the child gets inserted in the database as a orphan.
It can cause SQL errors later on, other transactions/constraints dont expect the orphans.
Is this expected behaviour or a bug?
The text was updated successfully, but these errors were encountered: