Deleting unreferenced child records with nhibernate

Posted by Chev on Stack Overflow See other posts from Stack Overflow or by Chev
Published on 2009-09-07T12:10:05Z Indexed on 2010/06/02 3:03 UTC
Read the original article Hit count: 301

Filed under:
|

Hi There

I am working on a mvc app using nhibernate as the orm (ncommon framework)

I have parent/child entities: Product, Vendor & ProductVendors and a one to many relationship between them with Product having a ProductVendors collection Product.ProductVendors.

I currently am retrieving a Product object and eager loading the children and sending these down the wire to my asp.net mvc client.

A user will then modify the list of Vendors and post the updated Product back. I am using a custom model binder to generate the modified Product entity. I am able to update the Product fine and insert new ProductVendors.

My problem is that dereferenced ProductVendors are not cascade deleted when specifying Product.ProductVendors.Clear() and calling _productRepository.Save(product).

The problem seems to be with attaching the detached instance. Here are my mapping files:

Product

<?xml version="1.0" encoding="utf-8" ?>

<id name="Id">
  <generator class="guid.comb" />
</id>

<version name="LastModified"
                unsaved-value="0"
                column="LastModified"
                 />

<property name="Name" type="String" length="250" />

ProductVendors

<?xml version="1.0" encoding="utf-8" ?>

<id name="Id">
  <generator class="guid.comb" />
</id>

<version name="LastModified"
                unsaved-value="0"
                column="LastModified"
                 />

<property name="Price" />

<many-to-one
  name="Product"
  class="Product"
  column="ProductId"
  lazy="false"
  not-null="true"
   />

<many-to-one
 name="Vendor"
 class="Vendor"
 column="VendorId"
 lazy="false"
 not-null="true"
   />

Custom Model Binder:

using System;

using Test.Web.Mvc; using Test.Domain;

namespace Spoked.MVC { public class ProductUpdateModelBinder : DefaultModelBinder { private readonly ProductSystem ProductSystem;

    public ProductUpdateModelBinder(ProductSystem productSystem)
    {
        ProductSystem = productSystem;
    }

    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var product = bindingContext.Model as Product;
        if (product != null)
        {
            product.Category = ProductSystem.GetCategory(new Guid(bindingContext.ValueProvider["Category"].AttemptedValue));
            product.Brand = ProductSystem.GetBrand(new Guid(bindingContext.ValueProvider["Brand"].AttemptedValue));

            product.ProductVendors.Clear();
            if (bindingContext.ValueProvider["ProductVendors"] != null)
            {
                string[] productVendorIds = bindingContext.ValueProvider["ProductVendors"].AttemptedValue.Split(',');
                foreach (string id in productVendorIds)
                {
                    product.AddProductVendor(ProductSystem.GetVendor(new Guid(id)), 90m);
                }
            }
        }
    } 
}

}

Controller:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Update(Product product)
    {
        using (var scope = new UnitOfWorkScope())
        {
            //product.ProductVendors.Clear();
            _productRepository.Save(product);
            scope.Commit();
        }
        using (new UnitOfWorkScope())
        {
            IList<Vendor> availableVendors = _productSystem.GetAvailableVendors(product);
            productDetailEditViewModel = new ProductDetailEditViewModel(product,
                                                                        _categoryRepository.Select(x => x).ToList(),
                                                                        _brandRepository.Select(x => x).ToList(),
                                                                        availableVendors);
        }
        return RedirectToAction("Edit", "Products", new {id = product.Id.ToString()});
    }

The following test does pass though:

 [Test]
    [NUnit.Framework.Category("ProductTests")]
    public void Can_Delete_Product_Vendors_By_Dereferencing()
    {
        Product product;
        using(UnitOfWorkScope scope = new UnitOfWorkScope())
        {
            Console.Out.WriteLine("Selecting...");
            product = _productRepository.First();
            Console.Out.WriteLine("Adding Product Vendor...");
            product.AddProductVendor(_vendorRepository.First(), 0m);
            scope.Commit();
        }
        Console.Out.WriteLine("About to delete Product Vendors...");
        using (UnitOfWorkScope scope = new UnitOfWorkScope())
        {
            Console.Out.WriteLine("Clearing Product Vendor...");
            _productRepository.Save(product); // seems to be needed to attach entity to the persistance manager
            product.ProductVendors.Clear();
            scope.Commit();
        }
    }

Going nuts here as I almost have a very nice solution between mvc, custom model binders and nhibernate. Just not seeing my deletes cascaded. Any help greatly appreciated.

Chev

© Stack Overflow or respective owner

Related posts about asp.net-mvc

Related posts about nhibernate