NHibernate cascade collection delete when insert new item to not empty collection - nhibernate

I have the following Fluent Mappings:
public ScanDeliverySessionMap()
{
Id(x => x.Id);
...
...
HasManyToMany(x => x.ToScanForms) <--- IList<Form> ToScanForms --->
.Table("ToScanForm")
.ParentKeyColumn("SessionId")
.ChildKeyColumn("FormId").Cascade.SaveUpdate();
}
public FormMap()
{
Id(x => x.Id).Column("FormID").GeneratedBy.Foreign("Log");
....
....
HasManyToMany(x => x.ScanDeliverySessions)
.Table("ToScanForm")
.ParentKeyColumn("FormId")
.ChildKeyColumn("SessionId").Inverse();
}
When I try to insert new Form to the ToScanForms collection
Everything seemingly works properly but watching on NHProf
I see that NH casacde DELETE over all ToScanForms items
and then NH INSERT the ToScanForms items including the new item.
Some screenshots:

That behaviour occurs because nhibernate doesn't know which entities in the collection are new and which are old, so he must delete everything and then re-insert them.
To prevent this is behaviour is quite simple: change your property to an ICollection and map your HasManyToMany as a set. You mapping would be changed to the following:
public ScanDeliverySessionMap()
{
Id(x => x.Id);
...
...
HasManyToMany(x => x.ToScanForms) //<--- ICollection<Form> ToScanForms --->
.AsSet()
.Table("ToScanForm")
.ParentKeyColumn("SessionId")
.ChildKeyColumn("FormId").Cascade.SaveUpdate();
}
public FormMap()
{
Id(x => x.Id).Column("FormID").GeneratedBy.Foreign("Log");
....
....
HasManyToMany(x => x.ScanDeliverySessions)
.AsSet()
.Table("ToScanForm")
.ParentKeyColumn("FormId")
.ChildKeyColumn("SessionId").Inverse();
}
Under the hood nhibernate will use Iesi Collections' HashSet, so now he knows which entities are new and which are old.

Related

Deleting child objects in HasMany() NHibernate relationship

I have a Product object which references an ordered list of Specification objects. When the Product gets updated, the associated list of Specifications are .Clear()'d and rebuilt (not new'd.)
The problem with Cascade.All() is that when the Product is updated, it creates 20 new Specification rows in the database, abandoning the 20 old ones. Cascase.AllDeleteOrphans() throws an error:
Additional information: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: Invax.Core.Models.Product.Specifications
Although this is what I want to happen.. It's true, the collection of Specifications are no longer referenced by the Product -- so delete these, right? Well, I have tried Inverse() to allow the Specification objects themselves handle the relationship but it didn't work either. Here is my current mappings which throw the aforementioned error.
Product Mapping:
public class ProductMap : ClassMap<Product>
{
public ProductMap ()
{
Id(x => x.Id);
Map(x => x.CatalogId).Not.Nullable();
Map(x => x.Name).Not.Nullable();
Map(x => x.UrlSlug).Not.Nullable();
Map(x => x.ShortDescription).Not.Nullable();
Map(x => x.Description).Not.Nullable();
Map(x => x.ImageFileName);
HasMany(x => x.Specifications).Cascade.AllDeleteOrphan().KeyColumn("ProductId");
References(x => x.Category).Column("Category");
}
}
Specification Mapping:
class SpecificationMap : ClassMap<Specification>
{
public SpecificationMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Value);
References(x => x.Product).Column("ProductReferenceId").Cascade.All();
}
}
I am open to any solution to this problem, including a more robust database design or data organization. I have to stick with Lists because the Specifications are ordered in a specific way.
Apparently too late for you, but if another one has this problem.
You should set the Cascade to Cascade.AllDeleteOrphan() because
Cascade.All() does not delete unreferenced child objects.
Ayende: Difference between cascades
The Inverse() solves the problem. Eg:
HasMany(x => x.Specifications).Cascade.AllDeleteOrphan().Inverse();

Parent-Child mapping with Fluent NHibernate does not insert children

I'm trying to map a very basic parent-child relation with Fluent NHibernate.
However, when analyzing the SQL, only the parent-INSERT statement is created.
The situation is a simple class with a list of other classes. No relation back to the parent is needed. The children needs to be inserted/updated when the parent is inserted/updated.
var room = new Room();
room.Name = "Room1";
room.Courses.Add(new Course(){ Name = "Course1"});
room.Courses.Add(new Course(){ Name = "Course2"});
using (var session = sessionFactory.OpenStatelessSession())
{
using (var transaction = session.BeginTransaction())
{
session.Insert(room);
transaction.Commit();
}
}
The mapping looks like this.
public class RoomMapping : ClassMap<Room>
{
public RoomMapping()
{
Table("Rooms");
Id(x => x.Id)
.GeneratedBy.SeqHiLo("seq_rooms", "1000");
Map(x => x.Name);
HasMany(x => x.Courses)
.Cascade.All();
}
}
public class CourseMap : ClassMap<Course>
{
public CourseMap()
{
Table("Courses");
Id(x => x.Id)
.GeneratedBy.SeqHiLo("seq_courses", "1000");
Map(x => x.Name);
}
}
I already played with multiple options of the HasMany, however non with any success.
Sorry people. I just found it out.
I'm working in a Stateless session. So no relationships are managed ;)

Fluent Mapping : using join-table and cascades

having a little trouble with a mapping for the following table setup currently:
Shop
[1] [1]
/ \
[n] [n]
Category-[m]---[n]-Article
The behaviour should be the following :
1 - when deleting a shop, all Articles and Categories Should be deleted
2 - when deleting a Category, related Articles should be unassigned but not deleted
3 - when deleting an Article, related Categories should be unassigned but not deleted
Here's the current mapping:
public class ShopMap: ClassMap<Shop>
{
public ShopMap()
{
this.Table("shop");
Id(x => x.Id).Column("id").GeneratedBy.Native();
Map(x => x.Name).Column("name");
HasMany(x => x.Categories).Cascade.AllDeleteOrphan;
HasMany(x => x.Articles).Cascade.AllDeleteOrphan;
}
}
public class CategoryMap: ClassMap<Category>
{
public CategoryMap()
{
this.Table("category");
Id(x => x.Id).Column("id").GeneratedBy.Native();
Map(x => x.Name).Column("name");
References(x => x.Shop);
HasManyToMany(x => x.Articles).Cascade.AllDeleteOrphan()
.Table("article_category")
.ChildKeyColumn("article_id")
.ParentKeyColumn("category_id")
.Inverse();
}
}
public class ArticleMap: ClassMap<Article>
{
public ArticleMap()
{
this.Table("article");
Id(x => x.Id).Column("id").GeneratedBy.Native();
Map(x => x.Name).Column("name");
References(x => x.Shop);
HasManyToMany(x => x.Categories).Cascade.All()
.Table("article_category")
.ParentKeyColumn("article_id")
.ChildKeyColumn("category_id");
}
}
When deleting a Category (Session.Delete()), NH tries to delete the related Articles as well. Changing the Cascade-Mode to SaveUpdate will fix this, but will leave the entries in the link table *article_category*. Summing up : Cascade.SaveUpdate is too lazy, Cascade.All is too eager.
I tried everything that came to my mind in the mappings, but couldn't find a correct way to map this (rather simple schema).
Any ideas on how to (fluently) map this are greatly appreciated!
Thanks in advance
Sebi
The entries are left in the link table because Category.Articles is defined as the inverse side of the relationship. You need to remove the Category from Article.Categories before deleting it in order for the link record to be removed.

fluent nhibernate: INSERT error when saving a new entity with a child entity

I am trying to save a new entity 'Post' with 1 item added to its 'Revisions' List.
A Post can have many PostRevisions, and a PostRevision can only have one Post.
I have tried several ways of mapping the PostRevisions, my PostRevisionMap is as follows:
public PostRevisionMap()
{
Id(x => x.PostRevisionId, "PostRevisionId");
Map(x => x.Created, "CreateDateTime").Not.Nullable();
/// SOME OTHER STUFF HERE
References(x => x.Post, "PostId"); // OPTION 1
References(x => x.Post,"PostId").ForeignKey("PostId").PropertyRef(d => d.PostId); // OPTION 2
HasOne<Post>(x => x.Post).ForeignKey("PostId").Cascade.All().PropertyRef(x => x.PostId); // OPTION 3
}
When calling SaveOrUpate I get a different errors
OPTION 1 & 3 cause
The INSERT statement conflicted with the FOREIGN KEY constraint "PostId".
OPTION 2 causes
NHibernate.HibernateException : Unable to resolve property: PostId
My PostMap is as follows:
public PostMap()
{
Id(x => x.PostId).Column("PostId");
HasMany(x => x.Revisions)
.Cascade.All()
.Table("PostRevisions")
.KeyColumn("PostId")
.ForeignKeyConstraintName("FK_PostRevision_Post");
/// OTHER STUFF
}
Can anyone point me in the right direction as I can not spot the issue.
TIA
Try this:
PostMap
HasMany(x => x.Revisions)
.Inverse()
.Cascade.All();
PostRevisionMap:
References(x => x.Post);

Fluent nhibernate map subclass in rc

I compiled the fluent nhibernate 1.0 rc with nhibernate 2.1 and had several warnings after the errors were fixed.
Fluent nhibernate tells me to use a separate Subclass-map instead of JoinedSubclass.
Current Mapping:
public class ClientMap : ClassMap<Client>
{
public ClientMap()
{
LazyLoad();
Id(x => x.Id);
//some boring stuff in between
JoinedSubClass<Company>("Id", m =>
{
m.LazyLoad();
m.Map(x => x.Name);
m.Map(x => x.Form);
});
}
The classes are inherited (company : client).
I tried the new mapping as follows:
public class CompanyMap : SubclassMap<Company>
{
CompanyMap()
{
LazyLoad();
Map(x => x.Name);
Map(x => x.Form);
}
}
After this change I don't get any companies, I'm not sure about how hibernate correctly knows what to do. Before I sayd "look, I have this class and the subclass I give you straight away in your mapping" and now: "Here are two mappings, figure by yourself, thx" :)
Any advices how to get the new subclass-mapping corrected?
Update:
Now I figured out that this works for saving data but the fk ID is not written to the child. How do I map the FK? The field name is Client_id, as nhibernate expects fk field names..