Question

I have a few objects.

  1. Project Object - this represents a basic project

  2. ProjectDependency Object - this is mapping object that maps one project to another project (the dependent project). See the nhibernate relationships:

here is the ProjectDependencies mapping class:

  public class ProjectDependencyMap
  {
      public ProjectDependencyMap()
      {
        References(x => x.Project).Not.Nullable().Fetch.Join();
        References(x =>.DependencyProject).Not.Nullable().Column("DependencyProjectId").Fetch.Join();
    }
}

and here is the project map file:

public class ProjectMap
{
    public ProjectMap()
    {
        HasMany(x => x.ProjectDependencies).AsBag().Inverse().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

        HasMany(x => x.ProjectDependentOf).KeyColumn("DependentProjectId").AsBag().Inverse().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

    }
}

normally this works fine but sometimes when i go to delete a project using:

 var project = Model.GetProject(id);
 Repository.Delete(project);
 Repository.Commit()

I get this error:

deleted object would be re-saved by cascade (remove deleted object from associations)[ProjectDependency#324]

Can someone help clarify what the issue is here and if the way i am using the mapping above is incorrect

Was it helpful?

Solution

For the purposes of this answer, I'm assuming that Project.ProjectDependencies is the other side of the ProjectDependency.Project relationship, not the ProjectDependency.Dependent relationship. (now say that 3 times fast)

Consider the following objects:

var projectA = new Project();
var projectB = new Project();
var dep1 = new ProjectDependency
{
    Project = projectA,
    Dependent = projectB
};
projectA.ProjectDependencies.Add(dep1);

... and save them all to the database. Now, let's delete one.

session.Delete(projectA);

Everything works fine. NHibernate cascades the delete across the ProjectDependencies collection, so projectA and dep1 are both deleted. projectB is left in the database.

OK, let's reset. Let's start over with projectA, projectB, and dep1 in the database. Additionally, let's add these:

var projectC = new Project();
var dep2 = new ProjectDependency
{
    Project = projectC,
    Dependent = projectA
};
projectC.ProjectDependencies.Add(dep2);

What happens when we delete projectA now?

session.Delete(projectA);

Everything tries to proceed as before. dep1 will be deleted by the cascade. However, there's nothing there to clean up dep2, hence the error. dep2 still has a reference to projectA, which we are trying to delete.

The solution suggested by the error message is to clean up those relationships before trying to delete the entity. However, how are you going to do this? Starting from projectA, how do we find dep2 and projectC to clean them up?

Two options:

  • Execute a query: session.Query<ProjectDependency>().Where(x => x.Dependent == projectA), then manually clean up the relationships.
  • Add another collection to Project:
HasMany(x => x.DependentProjects)
    .KeyColumn("DependentProjectId")
    .AsBag()
    .Inverse()
    .Cascade.AllDeleteOrphan();

... and then NHibernate would take care of the cleanup for you.

OTHER TIPS

I'm assuming that the relationship is many-to-many and ProjectDependency is an entity that represents the join table, it's hard to tell from your question. I'm assuming tables:

Project ( ProjectId)

ProjectDependency (ProjectId, DependentProjectId) with foreign keys back to Project

To delete a Project record, you have to delete any ProjectDependency records that reference it. That means deleting the ProjectDependency records for which the Project is referenced by ProjectDependency.ProjectId and those referenced by ProjectDependency.DependentProjectId. To do this in code, you can simply remove the referenced entities from the collections. Since you're using the repository pattern, this operation should be implemented in the Delete method

public void Delete(Project project)
{
    using (var txn = _session.BeginTransaction())
    {
        project.ProjectDependencies.Clear();
        project.DependentDependentOf.Clear();
        _session.Delete(project);
        txn.Commit();
    }
}

Showing Some code inside both method calls below would help :

Repository.Delete(project);
Repository.Commit();

But here are my 2 cents,

Remove the .Inverse() from the ProjectDependencies HasMany mapping. If you want nhibernate to manage the state from the "one" side in a "one-to-many" relation-ship, you should not be using .Inverse() in the HasMany mapping.

So it becomes like this :

public class ProjectMap
{
    public ProjectMap()
    {
        HasMany(x => x.ProjectDependencies).AsBag().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

        HasMany(x => x.ProjectDependentOf).KeyColumn("DependentProjectId").AsBag().Inverse().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

    }
}

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top