C# – NHibernate Error Message: object references an unsaved transient instance – save the transient instance before flushing

cfluent-nhibernatenhibernatenhibernate-mapping

I'm using nHibernate and trying to add some master-detail data. The database diagram is attached here.
Database diagram

The following is the code for adding a category along with two products:

//Category
namespace Sample.CustomerService.Domain
{

    public class Category {
        public Category() {
            Products = new List<Product>();
        }
        public virtual int CategoryID { get; set; }
        public virtual IList<Product> Products { get; set; }
        public virtual string Name { get; set; }
        public virtual string Unit { get; set; }

        public virtual void AddProduct(Product product)
        {
            Products.Add(product);
        }
    }
}

//CategoryMap
namespace Sample.CustomerService.Domain
{

    public class CategoryMap : ClassMap<Category> {

        public CategoryMap() {
            Table("Category");
            LazyLoad();
            Id(x => x.CategoryID).GeneratedBy.Identity().Column("CategoryID");
            Map(x => x.Name).Column("Name").Not.Nullable().Length(50);
            Map(x => x.Unit).Column("Unit").Not.Nullable().Length(3);
            HasMany(x => x.Products).KeyColumn("CategoryID");
        }
    }
}

//--------------------------------------------------------------------------------------------
//Product
namespace Sample.CustomerService.Domain
{

    public class Product {
        public Product() {
        }
        public virtual int ProductID { get; set; }
        public virtual Category Category { get; set; }
        public virtual string Name { get; set; }
        public virtual decimal UnitPrice { get; set; }

    }
}

//ProductMap
namespace Sample.CustomerService.Domain {


    public class ProductMap : ClassMap<Product> {

        public ProductMap() {
            Table("Product");
            LazyLoad();
            Id(x => x.ProductID).GeneratedBy.Identity().Column("ProductID");
            References(x => x.Category).Column("CategoryID");
            Map(x => x.Name).Column("Name").Not.Nullable().Length(50);
            Map(x => x.UnitPrice).Column("UnitPrice").Not.Nullable();
        }
    }
}

//----------------------------------------------------------------------------------------------
//Program
namespace WindowsHibernateTest
{
    public partial class TestClass : Form
    {
        public TestClass()
        {
            InitializeComponent();
            CreateNewProduct();
        }

        public void CreateNewProduct()
        {
            try
            {
                var sessionFactory = CreateSessionFactory();
                using (var session = sessionFactory.OpenSession())
                {
                    using (var sqlTrans = session.BeginTransaction())
                    {
                        Category newGold = new Category() { Name = "Gold", Unit = "GRM" };

                        Product ngOrn = new Product() { Name = "Bangles", UnitPrice = 1000.10M };
                        Product ogOrn = new Product() { Name = "Rings", UnitPrice = 2000.10M };

                        AddProductsToCategory(newGold, ngOrn, ogOrn);

                        session.SaveOrUpdate(newGold);

                        sqlTrans.Commit();
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private static ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure().Database(MsSqlConfiguration.MsSql2005.
                ConnectionString("Data Source=MyDB; Initial Catalog=NHibernateTest; Trusted_Connection=true;")).Mappings(m => m.FluentMappings.AddFromAssemblyOf<Program>()).BuildSessionFactory();

        }

        public static void AddProductsToCategory(Category category, params Product[] products)
        {
            foreach (var product in products)
            {
                category.AddProduct(product);
            }
        }

    }
}

In the line sqlTrans.Commit(), I'm getting the following error:

object references an unsaved transient instance – save the transient
instance before flushing. Type: Sample.CustomerService.Domain.Product,
Entity: Sample.CustomerService.Domain.Product

I've Googled for quite sometime and didn't get convincing solutions for this. I've tried this, this and this, but could not able to resolve this error. Please help me to resolve this error. Thanks in advance.

Best Answer

I think you need to save the products before you save the category because you don't have a Cascade on the HasMany. And you need to change the add product methods to keep the relation

foreach (var product in products)
{
    category.AddProduct(product); 
    product.Category = category;
}