1

Extreme newbie question. I have my database (SQL Server) set up to cascade deletes for my relationships so if you delete a parent entity all the children are deleted as well (ON DELETE CASCADE). I want this to be reflected in my automapped Fluent NHibernate setup. However, when I try to delete a child entity NHibernate is instead attempting to set the relationship key to NULL.

The database is super-simple: (-- for "one", -< for "many")

User ---< UserCode >--- Code >--- CodeGroup

When I delete a CodeGroup, the deletes should cascade down to Code and UserCode. When I delete a Code, it should just cascade to UserCode but leave CodeGroup untouched.

My entities (properties removed for clarity):

public class User {
    public virtual IList<Code> FoundCodes { get; private set; }
}
public class Code {
    public virtual IList<User> UsersWithCode { get; private set; }
    public virtual CodeGroup CodeGroup { get; set; }
}
public class CodeGroup {
    public virtual IList<Code> Codes { get; private set; }
}

Here's how the SessionFactory looks:

var _sessionFactory =
    Fluently.Configure()
    .Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005.ConnectionString(c => c.FromConnectionStringWithKey("db")).ShowSql())
    .Cache(csb => csb.UseQueryCache())
    .Mappings(m => 
      m.AutoMappings.Add(
       AutoMap.AssemblyOf<Code>(new AutomappingConfiguration())
       .Override<User>(map => map.HasManyToMany(u => u.FoundCodes).Table("UserCode"))
       .Override<Code>(map => map.HasManyToMany(c => c.UsersWithCode).Inverse().Table("UserCode"))
       .Conventions.Add(new CustomForeignKeyConvention())))
    .BuildSessionFactory())

But when I do this:

using (var tx = _db.BeginTransaction())
{
    var codeGroup = _db.Load<CodeGroup>(id);
    _db.Delete(codeGroup);
    tx.Commit();
}

I get this:

could not delete collection: [MidnightRoseRace.Data.Entities.CodeGroup.Codes#8]
    [SQL: UPDATE [Code] SET CodeGroupId = null WHERE CodeGroupId = @p0]
Cannot insert the value NULL into column 'CodeGroupId', table 'MyNamespace.dbo.Code'; 
    column does not allow nulls. UPDATE fails.
The statement has been terminated.

All it has to do is delete, but instead it's trying to set a non-nullable foreign key to null. What's going on?

Daniel Schilling
  • 4,829
  • 28
  • 60
roufamatic
  • 18,187
  • 7
  • 57
  • 86

2 Answers2

2

Deletes do not cascade by default in NHibernate. Set cascading on codeGroup.Codes relationship like this:

AutoMap.AssemblyOf<Code>(new AutomappingConfiguration())
    // existing overrides
    .Override<CodeGroup>(
        map => map.HasMany(c => c.Codes).Cascade.AllDeleteOrphan().Inverse())

And similarly for other relations that you need to be influenced.

Edited by OP: Just needed that ".Inverse()" at the end. Related to this question: key-many-to-one and key-property association: nhibernate won't DELETE items from set

Community
  • 1
  • 1
NOtherDev
  • 9,542
  • 2
  • 36
  • 47
  • `AllDeleteOrphan()` isn't a member of `Cascade` -- perhaps you mean "`All()`" ? – roufamatic Oct 25 '11 at 18:58
  • You're right. `AllDeleteOrphan` is for *one-to-many* only, `All` will go here. – NOtherDev Oct 25 '11 at 19:01
  • Nope, this isn't working -- `An association from the table CodeGroup refers to an unmapped class: System.Collection.Generic.IList``1[[MyNamespace.Code, ...]]` – roufamatic Oct 25 '11 at 19:04
  • OK, this IS *one-to-many*, so it was correct before, but with different relation mapping. See corrected answer. Sorry for mess. – NOtherDev Oct 25 '11 at 19:17
  • Same error. Hey, is there any significance to the message "could not delete collection" ? That seems ominous -- it shouldn't be trying to delete a collection, just an entity. – roufamatic Oct 25 '11 at 20:16
  • I've got it: need to add `.Inverse()` to the end of that `AllDeleteOrphan()` call. See http://stackoverflow.com/questions/1541660/key-many-to-one-and-key-property-association-nhibernate-wont-delete-items-from . – roufamatic Oct 25 '11 at 20:20
1

You might be hitting this issue. Unless you're running 3.2.0Beta2 or later, if you want to cascade deletes you must either:

  1. make the child's FK nullable; or
  2. create a inverse relationship (that is, the child must have a mapped reference to the parent).

As you can see from the ticket this has been longstanding (and much upvoted) issue that has very recently been fixed.

Tim Scott
  • 15,106
  • 9
  • 65
  • 79