Can't cascade delete or update in Nhibernate if several Entities has references to the same source - nhibernate

I have such mapping of ChargeOperations(left table) and Distributions(right table):
In code mapping of ChargeOperations looks like:
HasMany(x => x.Distributions).Table("ShadowDistributions").KeyColumn("SourceId").Cascade.All().Inverse();
ShadowDistributions - is a right table. x.Distributions is a just a list of Distributions(right table). X - is ChargeOperation(left table)
Mapping of Distributions (right table)
References(x => x.Source).Nullable().Column("SourceId").Not.LazyLoad();
References(x => x.Dest).Nullable().Column("Dest").LazyLoad().Fetch.Join().Cascade.All();
So, I want to delete just one row from Distributions (right table).
And the applications throws different mapping exceptions like "Transaction could not commit because of a failed resource : deleted object would be re-saved by cascade (remove deleted object from associations)[ChargeOperation#58]"" or "Unexpected row count: 0; expected: 1" and so on.
I use cascade for creating entities and it works great, but for delete I had to
clean all references in the right table, and after that save all types of entities separately. If not, I'll get errors.
But I'd like to use cascade saving. How can I realize it?
Possible variants after save:
deleted just one record from right table. all records in left
table are exist
deleted just two records from right table. all
records in left table are also deleted

Change your Cascade to Cascade.SaveUpdate() if you don't wish to delete any of the referenced entities.
In my opinion you shouldn't be trying to delete a parent (ChargeOperations) whenever you delete one of the children (Distributions)
Example
public SomeMethod()
{
using(ISession session = ... //Get my session from somewhere)
{
Distribution childToDelete = ... //Get the distribution to delete
ChargeOperation parent = ... //Get the parent of the distribution we are deleting
parent.Distributions.Remove(childToDelete);
//Since the parent is in session just flush the session to apply changes
session.Flush();
}
}

Related

How to delete related data with no foreign key using Entity Framework Core

I wish to use this in my database context class:
OnDelete(DeleteBehavior.Cascade)
But it's on a SQL Server table with no foreign key.
My parent table, TransplantList, has a column heartId, which corresponds to a table HeartList which looks like this in SQL Server:
heartId
heartName
heartLocation
heartType
TransplantList can only have one heartId per row.
When I delete a row in TransplantList, I also want to delete (if it's not NULL) the heartId associated with it.
I tried adding this to my context database:
Entity.OnDelete(DeleteBehavior.Cascade);
But that just gives me an error.
Is there anyway to do this?
Thanks!
Entity Framework core cannot cascade delete child data for you if the parent and child tables don't have any relationship and foreign key. So you have to get child data manually before deleting child and parent data.
var heart = context.Hearts.Where(x => x.HeartId == transparent.HeartId).FirstOrDefault();
if (heart != null)
context.Hearts.Remove(heart);
context.Transparents.Remove(transparent);
context.SaveChanges();
The Cascade operation is not a part of Entity Framework. It only defines it. The operation itself is performed solely by the database server based on the foreign-key relationship. If your database doesn't define that relationship, there's nothing Entity Framework can do.
As for -
How to delete related data with no foreign key using Entity Framework
Core
check if a related entity exist. If it does, delete it -
var transplant = await context.Transplants.FindAsync(id);
if(transplant != null)
{
context.Transplants.Remove(transplant);
// check if heartId column has a non-null value
if(transplant.HeartId != null)
{
// query the Heart entity with heartId
Heart heart = await context.Hearts.FirstOrDefaultAsync(p => p.HeartId == transplant.HeartId);
// if a Heart entity is found, Delete it
if(heart != null)
context.Hearts.Remove(heart);
}
await context.SaveChangesAsync();
}

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.

NHibernate many-to-many and deleting an item

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();
}
}

(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.

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.