I have some problem with cascade all (orphan) and delete the old objcet from the database.
Example:
I have an class A which contains an object of class B. Now, when I create an object of class A and save it, everything works fine. When I call the method SetValueOfB(int i)
and save the object A again, the old object B is still in the database.
Must the association between the classes always be directional (for every HasMany/Reference/HasOne...)? (But object b has nothing to know about object a)
Is there a way to solve the problem with unidirectional association?
Do I need a one-to-one mapping? Because the object B can only belong to object A (A is a parameter and B is a value).
Here is a failing test:
using System.Collections.Generic;
using System.Data;
using System.IO;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Mapping;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
namespace ReferenceCascade.Test
{
public class CascadeTest
{
private const string DbFile = "firstProject.db";
[Test]
public void checkCascadeAll()
{
var sessionFactory = CreateSessionFactory();
A testee = new A(new B(1));
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(testee);
transaction.Commit();
}
}
testee.SetValueOfB(2);
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(testee);
transaction.Commit();
}
}
using (var session = sessionFactory.OpenSession())
{
using (session.BeginTransaction())
{
IList<B> stores = session.CreateCriteria(typeof(B))
.List<B>();
Assert.That(stores.Count, Is.EqualTo(1));
}
}
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard.UsingFile(DbFile).IsolationLevel(IsolationLevel.ReadCommitted))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<CascadeTest>())
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private static void BuildSchema(Configuration config)
{
// delete the existing db on each run
if (File.Exists(DbFile))
{
File.Delete(DbFile);
}
// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config)
.Create(false, true);
}
}
public abstract class Entity
{
public const long InitialId = 0;
private readonly long _id;
protected Entity()
{
_id = InitialId;
}
public virtual long Id
{
get { return _id; }
}
}
public class A : Entity
{
private B _b;
public A()
{
}
public A(B b)
{
_b = b;
}
public virtual void SetValueOfB(int i)
{
_b = new B(i);
}
public virtual B B
{
get { return _b; }
}
}
public class B : Entity
{
private readonly int _i;
public B()
{
}
public B(int i)
{
_i = i;
}
public virtual int I
{
get { return _i; }
}
}
public class EntityMap<T> : ClassMap<T> where T : Entity
{
public EntityMap()
{
Id(x => x.Id).GeneratedBy.HiLo("33878").Access.CamelCaseField(Prefix.Underscore);
}
}
public class AMap : EntityMap<A>
{
public AMap()
{
Table("A");
References(x => x.B).Not.LazyLoad().Cascade.All().Access.CamelCaseField(Prefix.Underscore);
}
}
public class BMap : EntityMap<B>
{
public BMap()
{
Table("B");
Map(x => x.I).Not.LazyLoad().Access.CamelCaseField(Prefix.Underscore);
}
}
}
Or here is the project: vs project
We haven't found a way to solve the problem. In NHibernate version 4.1, the problem will be fixed and it is possible to use cascade=all-delete-orphan with many-to-one. See here: https://nhibernate.jira.com/browse/NH-1262