Reputation: 11
I am running into a problem with NHibernate and I can't quite figure out the best solution. I have a Customer class with a child collection, and I am trying to delete an item from the collection by removing it from the list.
Class and mappings are below:
public class Customer : Entity<Customer>
{
public virtual IList<WishlistProduct> Wishlist { get; set; }
public Customer()
{
Wishlist = new List<WishlistProduct>();
}
public virtual bool RemoveFromWishlist(int id)
{
bool removed = false;
for (int x = Wishlist.Count - 1; x >= 0; x--)
{
if (Wishlist[x].Product.Id == id)
{
Wishlist[x].Customer = null;
Wishlist.RemoveAt(x);
removed = true;
}
}
return removed;
}
}
Mappings:
internal class CustomerMapping : EntityMapping<Customer>
{
internal CustomerMapping()
{
HasMany(x => x.Wishlist)
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
internal class WishlistProductMapping : EntityMapping<WishlistProduct>
{
internal WishlistProductMapping()
{
References(x => x.Customer);
}
}
To delete the child I am doing something like this:
customer.RemoveFromWishlist(1);
I am using the following session management technique:
builder.Register(x => MyApplication.SessionFactory.OpenSession()).As<ISession>()
.InstancePerHttpRequest()
.OnRelease(x =>
{
x.Flush();
x.Close();
});
I can see from the SQL logs that this is indeed generating the correct DELETE statement to remove the record, however in the OnRelease callback I am getting a StaleObjectStateException: "Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)". In the exception detail I can see it is borking on the child object that is being deleted.
Why is this occurring? I don't see how the child object is being modified anywhere prior to Flush being called. I am not doing anything to the parent object or child object other than removing the child from the list.
I know I could just delete the child object individually but the business logic seems much more natural to be able to remove the child from the parent's list and have the delete taken care of.
Any help or insight is appreciated, thanks.
Upvotes: 1
Views: 1752
Reputation: 13344
In addition to @jamie-ide answer.
You are using wrong cascade type in that situation. You need to use AllDeleteOrphan():
internal class CustomerMapping : EntityMapping<Customer>
{
internal CustomerMapping()
{
HasMany(x => x.Wishlist)
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
Upvotes: 2
Reputation: 49251
You need to maintain both sides of the relationship by removing the item from the collection and null'ing the reference to its parent.
public virtual bool RemoveFromWishlist(int id)
{
bool removed = false;
for (int x = Wishlist.Count - 1; x >= 0; x--)
{
if (Wishlist[x].Product.Id == id)
{
Wishlist[x].Customer = null;
Wishlist.RemoveAt(x);
removed = true;
}
}
return removed;
}
Upvotes: 0