Question

I have a many-to-one relationship between objects Product and Supplier. I need to be able to delete Supplier without deleting the Products that belong to it.

Here is a simplified version of classes:

public class Supplier {
    public virtual IList<Product> Products { get; protected set; }
}

public class Product {
    // Product belongs to a Category but Supplier is optional
    public virtual Supplier Supplier { get; set; }
    public virtual Category Category { get; set; }
}

I'm using FluentNHibernate, but here are the mappings it produces:

<bag name="Products" cascade="save-update" inverse="true">
      <key column="SupplierID" />
      <one-to-many class="Me.Product, Me, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>

<many-to-one name="Supplier" column="SupplierID" />

This creates a foreign key on the Products table, so when I attempt to do a straight delete on a Supplier I get a foreign key constraint error. I tried changing cascade to 'all', in the hope that it might only delete the relationship, but it deleted all Products and their other associated objects.

The only way I can see to solve this right now is to iterate the Products collection of Supplier and set the Supplier property to null. Is there a way I can achieve this behaviour through the mapping?

Was it helpful?

Solution

The mapping properties only take effect when the entity is actually loaded, and when you are not querying through HQL. As an example, if you specify Cascade=ALL, if you delete a supplier with the query "delete from Supplier where id=:id", you will probably get the same FK constraint failure, because the hql doesn't trigger (programmatic) cascades.

It seems that the Products are the owning side of the relation, which is good. I think you have two choices:

  • Code some method on the Supplier to iterate through all Products and set the Product's supplier to null, and use this method before deleting any Suppliers
  • Before issuing a Supplier delete, make sure your DAO sets the product's supplier to null

Example:

public int Delete(Supplier s) {
    return Session.CreateQuery("udpate Product set Supplier = null where Supplier = :supplier")
        .SetParameter("supplier", s)
        .ExecuteUpdate();
}

OTHER TIPS

DON'T DO THIS.

You have an implicit or explicit assertion in your model that all Products have a Supplier. The foreign key is there to enforce this condition. Removing the Supplier while keeping the Products violates your model and will probably cause much of your code, which relies on this always being true, to fail.

The one thing you can do, you've already discovered: for every Product that has this Supplier, you can set the Product's Supplier null. This won't violate your condition, but it's the same as saying "we don't know who the Supplier of this Product is" and may cause code failures.

Why do you want to do this?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top