NHibernate many-to-many and deleting an item - nhibernate

I've got a many-to-many association between Lists and ListItems: a List knows about its Items, but a ListItem doesn't know about the containing lists. The cascade is saveupdate.
So, whenever I'm trying to delete a ListItem entity, I'm getting an SQLException saying I'm breaking the referential integrity. NHibernate tries to delete my ListItem without deleting the corresponding row in the linking table. The question is, is it possible to instruct NHibernate to delete my ListItem without breaking the referential integrity?
In case I have to manually remove the item from all containing lists, how do I properly do that?
Thanks a lot for any advice.
ulu

You need to set the mapping on the child to inverse=true. From another thread:
When you call SaveOrUpdate NHibernate
first deletes all of the child
objects. Then, because neither
relationship is marked as inverse,
NHibernate also tries to set the
foreign key column in your child table
to null. Since the rows have already
been deleted, you receive the second
error. You need to set inverse=true on
one side of your relationship to fix
this. This is usually done on the one
(primary key or parent) side. If you
do not do this, NHibernate will make
the appropriate updates for each side
of the relationship.
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Staff)
.Inverse() // Magic code!
.Cascade.All();
}
}

Related

Fluent Nhibernate --- How to make it NOT update the other table

I have a [User] table/class and a [Company] table/class and there is a link-table [UserCompany] between them.
When editing a User, beside basic information people also could change that user's access Companies, so I do the map like this in UserMap.cs:
HasManyToMany(u => u
.Companies)
.Cascade.SaveUpdate()
.Table("UserCompany")
.ParentKeyColumn("UserId")
.ChildKeyColumn("CompanyCode")
.Not.LazyLoad();
Also in CompanyMap.cs I set inverse like this:
HasManyToMany(c => c.Users)
.Inverse()
.Table("UserCompany")
.ParentKeyColumn("CompanyCode")
.ChildKeyColumn("UserId");
The problem now is: I could update [User] information/table, plus the linking data in [UserCompany] table. However, the Fluent Nhibernate also update the [Company] table which I don't need at all.
Is there any way I could let FN not update Company table?
To stop cascading updates just remove the
.Cascade.SaveUpdate()
from your Many-to-many mapping.
It could be a bit confusing. In comparison with the cascading used on <list>s and <map>s. In that case, the update is done directly on the child table (parent has more children... child contains the ParentId - cascade is reasonable to do operation on the child record)
But here we are working with a pair table. The relation is stored there. And this table will be always managed by NHibernate (implicit cascading).
The setting .Cascade.SaveUpdate() goes to other end of the many-to-many relation. To company table in our case. It could be handy.. but you can omit that and get everything running as expected.

(Fluent) NHibernate: force foreign key to null on delete

I have a standard parent - child (1:many) relationalship, configured using Fluent NHibernate:
On the parent side:
HasMany(x => x.Items).Inverse().Cascade.All();
and on the child side:
Map(x => x.ItemCategory).Nullable().Index("idx_item_category").Not.LazyLoad()
(Edit in response to epitka's comment:)
The record is deleted by calling
session.Delete(item_category)
This is the only operation done in the transaction.
(End Edit)
Currently when I delete an ItemCategory record it cascade the delete to all the items, which appears to be working as expected according to the documentation.
What I want is for Item.ItemCategory to be set to null automatically when the ItemCategory record is deleted.
I can only seem to turn off the cascade completely, which leads to a broken database (item's referencing a missing category). So, currently I have to do this manually which is a little more error prone than I'd like.
Is it possible to configure this behaviour?
session.Delete(item_category)
Whil it's not possible to do that out of the box, you can probably implement an IPreDeleteEventListener that fires an HQL update to set the Items' ItemCategory to null.

Update of many-to-many collection in Nhibernate causing multiple deletes in join table

I have a class that contains a collection that is mapped to a many-to-many database relationship using Fluent Nhibernate. The mapping is as below -
Table("Book");
Id(x => x.Id);
Map(x => x.Title);
Map(x => x.NumberOfPages);
HasManyToMany(x => x.Authors)
.Cascade.AllDeleteOrphan()
.Table("BookAuthor")
.ParentKeyColumn("BookId")
.ChildKeyColumn("AuthorId");
I then get an instance of this class and add an item to the authors collection using the code below -
var book = session.CreateCriteria(typeof(Book)).UniqueResult<Book>();
Author author = new Author {Name="Phil Moran",Age=51};
book.Authors.Add(author);
session.SaveOrUpdate(book);
The database is updated succesfully but Nhibernate updates the BookAuthor table by firstly deleting all the records within it linked to the updated book, then repopulating all the data plus the extra record required for the new author. Why is Nhibernate doing this? I would expect it to simply add a single record containing the book and author details to the many-to-many table (BookAuthor) and not perform any of the delete actions. Can I change this behaviour via my mappings?
That happens when the collection is mapped as a bag (I guess that's the default in FluentNH).
Use a set, list or idbag instead.

NHibernate cascade delete from another entity collection

I have these classes:
public class User
{
public IList<Order> LastOrders { get; set;}
}
public class Order {}
Where LastOrders is many-to-many map.
How do I tell (Fluent) NHibernate to remove Order from LastOrders collections for all users when I delete an Order? Is it possible?
That is (db save/load code skipped)
user.LastOrders.Add(order);
Session.Delete(order);
Assert(!user.LastOrders.Contains(order));
Currently I do it manually (lookup for users, update collection, save) before deletion. Without this, NHibernate can't delete Order because it is referenced by users' LastOrders.
You can safely delete the Order if the collection mapping is set to ignore missing rows.
This will leave orphaned rows in the collection table which will be ignored by NHibernate. These can be cleaned up in some batch process.
HasManyToMany(x => x.LastOrders)
.NotFound.Ignore();
This will give you faster deletes then your current approach. The disadvantage is that your collection tables will be inconsistent with your model for a time.

Fluent-NHibernate: How to a create a many-to-many relationship with a unique contraint

I want to create a many to many relationship, but I want to have in the new table(MessageReceivers) a unique contraint on both columns (AdvanceMessageId,UserId):
mapping.HasManyToMany(x => x.Receivers)
.WithParentKeyColumn("AdvanceMessageId")
.WithChildKeyColumn("UserId")
.Cascade.All()
.LazyLoad()
.WithTableName("MessageReceivers");
Thanks for help
Old post... but in case someone else arrives here looking for the answer:
You need to add .AsSet() to the HasManyToMany mapping definintion.
i.e.
mapping.HasManyToMany(x => x.Users)
.WithTableName("MessageReceivers")
.WithParentKeyColumn("UserId")
.WithChildKeyColumn("AdvanceMessageId")
.Inverse().AsSet();
This will setup an unique, composite primary key constraint on the link table that uses both columns.
(clustered index)
The down side is AsSet() cannont be used with collection properties of type IList, so no for loops without casting.
I have been using ICollection and instantiating them as HashSet for my applications and it works well.
More info on collection management with Fluent Nhibernate:
List: Ordered collection of entities, duplicate allowed. Use a .net IList in code. The index column will need to be mapped in NHibernate.
Set: Unordered collection of unique entities, duplicates not allowed. Use Iesi.Collection.ISet in code. It is important to override GetHashCode and Equals to indicate the business definition of duplicate. Can be sorted by defining a orderby or by defining a comparer resulting in a SortedSet result.
Bag: Unordered list of entities, duplicates allowed. Use a .net IList in code. The index column of the list is not mapped and not honored by NHibernate.
You should also map the inverse side of the relationship like
mapping.HasManyToMany(x => x.Users)
.WithTableName("MessageReceivers")
.WithParentKeyColumn("UserId")
.WithChildKeyColumn("AdvanceMessageId")
.Inverse();
In newest Fluent NHibernate you will have to change
WithTableName -> Table
WithParentKeyColumn -> ParentKeyColumn
WithChildKeyColumn -> ChildKeyColumn