2
votes

I have two simple entities named Country and City.

public class Country : Entity
{
    public Country()
    {
        Cities = new List<City>();
    }

    public virtual string Name { get; set; }
    public virtual IList<City> Cities { get; set; }
}

public class City : Entity
{
    public virtual Country Country { get; set; }
    public virtual string Name { get; set; }
}

The DB used is SQL Server and City has a foreign key to Country with cascade delete.

I am using Fluent NHibernate, this is the mapping configuration for the relation:

public CountryMap()
    {
            Id(x => x.Id, "IdCountry").GeneratedBy.Identity();

            Map(x => x.Name).Not.Nullable().Length(50);

            HasMany(x => x.Cities).KeyColumn("IdCountry").ForeignKeyConstraintName("FK_Cities_Countries")
                .Not.KeyNullable().Cascade.AllDeleteOrphan().ExtraLazyLoad();

            Table("Countries");
    }

public CityMap()
    {
            Id(x => x.Id, "IdCity").GeneratedBy.Identity();

            Map(x => x.Name).Not.Nullable().Length(50);

            References(x => x.Country, "IdCountry").ForeignKey("FK_Cities_Countries")
                .Not.Nullable().Not.Insert().Not.Update().Cascade.All().LazyLoad();


            Table("Cities");
    }

All works fine, but after delete a country, cities remain in parent collection and I want the cities to be removed from that collection. (As EF does)

The only way I found to get it working is refreshing the session (Clear, Evict...)

2
This is not possible, nor it should be. Deleting an entity will not alter what's in memory. In this case it is your responsibility to clear the collection.Onur Gumus

2 Answers

1
votes

Manual deletion of the collection items is not a solution. It in facts breaks the cascading feature.

In case we do have mapping Cascade.AllDeleteOrphan(), we should expect that deletion of Parent will deleted Children as well - no need to do more then delete Parent. Just ... NHibernate does not care about clearing that collection in memory (app server/application)

In case you are searching for long, stable, solid solution I would strongly suggest:

Split READ and WRITE operations

The current frameworks we have (like Web API) are helping us to go this direction. We should create set of operations:

  • PUT, POST, DELETE ... to handle client requests for data amendments
  • GET ... to retrieve data by ID or by Criteria (Find())

Each of these operations should have its own session, its own transaction, and should represent the unit of work. All or nothing. Either operation is successful and all is persisted, or not (rollback)

That will help our application being successful and growing in a longer/long run. We do a split. We care about

1) DELETE, UPDATE or INSERT (or more of them arround some root entity, could see more here). 2) we do READ operation - expecting only SELECT operations, therefore working with up-to-date data. Also check this, including comments

While this could be a bit out of scope, this cite form 9.8. Exception handling doc is also the clue:

...If the ISession throws an exception you should immediately rollback the transaction, call ISession.Close() and discard the ISession instance. Certain methods of ISession will not leave the session in a consistent state...

I wanted to demonstrate, that the operation (session, transaction) should have only one goal (WRITE, READ) and be as short as possible...

1
votes

With that kind of mapping, the easiest way to work is to remove the City from the Country collection and save the Country. With the cascade, the city will be deleted and the collection will be in the state you want.