Cascade not working as I expect
I'm relatively new to configuring NHibernate and have run in to a problem. I have the following entities :
public class Report
{
public virtual int Id { get; private set; }
public virtual string Name { get; private set; }
public virtual string Path { get; private set; }
public virtual string Alias { get; private set;}
public virtual IList<Tab> Tabs
{
get; private set;
}
}
public class Tab
{
public virtual string Name { get; private set; }
public virtual string Description { get; private set; }
public virtual IList<Section> Sections { get; set; }
public virtual Report ParentReport { get; private set; }
}
public class Section
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
I use Fluent NHibernate and Auto Mapping to map the relationships.
.Database(MsSqlConfiguration.MsSql2005.ShowSql().ConnectionString(c => c.Is(connectionString)))
.Mappings(map => map.AutoMappings.Add(AutoMap.AssemblyOf<Workbook>(configuration)
.Conventions.AddFromAssemblyOf<CascadeAll>()
The problem is that not matter what cascade options I choose in my CascadeAll class I can't get it to perform as I'd wish which is.
开发者_如何学编程When I remove a Tab from a given report, I want the sections on the tab to be removed from the database, and then the tab to be removed from the report in the database.
Cascade.All()
Removes the relationship between Report and Tab, but orphans the tab setting ParentReport_id to equal null.
Cascade.AllDeleteOrphan()
This removes all sections, tabs associated with the report, and then deletes the report, despite only removing one tab from the collection on the report.
Cascade.SaveUpdate()
Acts like All in that it orhpans the tab.
Incidentally these are all invoked by removing a tab from the tab collection on Report and then calling session.SaveUpdate(instance) > Where instance is the Report instance being updated.
Any idea how I can remedy this or what I'm doing wrong?
Thanks Rex, I suspect that your suggestion would work so time allowing I may give it a try. It was completely down to the relationship between Report and Tab, causing a OneToMany (Report-> Tab) and a ManyToOne (Tab -> Report). With the cascade options I chose it would either NUll the foreign key, or remove the tab, all other tabs and the report. This was with the AllDeleteOrphan.
When I remove the Tab -> Report ManyToOne relationship the orphan record got deleted fine without the Repotr being removed. In the end I went for a quick option of creating an explicit mapping file which used the HasMany(x => x.Tabs).... in the ReportMapping file & References(x => x.ParentReport)... on TabMapping.
Thanks for your help.
It sounds like you need to use a combination of these. You can write overrides for the special cases when you need something other than the default provided by your conventions.
If there's a pattern that comes up in your overrides that holds true a majority of the time, you can create a new convention to handle it, instead of having to specify overrides each time it happens.
I believe you'll want to use .AllDeleteOrphan()
for your Sections
collection, and use .All()
for your Tabs
collection.
In your case, I believe your override would look something like this.
public class ReportMappingOverride : IAutoMappingOverride<Report>
{
public void Override(AutoMapping<Report> mapping)
{
// You don't need this, but I'll leave it for an example...
//mapping.HasMany(x => x.Tabs).Cascade.All();
}
}
public class TabMappingOverride : IAutoMappingOverride<Tab>
{
public void Override(AutoMapping<Tab> mapping)
{
// You don't need this, but I'll leave it for an example...
//mapping.HasMany(x => x.Sections).Cascade.AllDeleteOrphan();
// This should prevent deletes from being cascaded to the Report
// (which would delete the report).
mapping.References(x => x.ParentReport).Cascade.SaveUpdate();
}
}
To use the overrides, your config would change a bit to look something like this.
.Mappings(map => map.AutoMappings.Add(AutoMap.AssemblyOf<Workbook>(configuration).UseOverridesFromAssemblyOf<ReportMappingOverride>()
Having the property back to the parent report shouldn't matter, but for testing you could take it out and see if that is the problem.
精彩评论