Search code examples
nhibernateormfluent-nhibernatemappingfluent-nhibernate-mapping

Self referencing entity in NHibernate gives object references an unsaved transient instance exception


I have a self referencing entity named Category. A category may have one or more child categories. A category may also have no childs.

I have made the entity and mapping but I keep getting this exception.

NHibernate.TransientObjectException : object references an unsaved transient instance - save the transient instance before flushing or set cascade action for the property to something that would make it autosave.

Am I doing something wrong with the mapping part?
Is it possible to save all Parent and Child objects at once?
Any help would be appreciated.

Following is the Code

POCO

public class Category
{
    private ICollection<Topic> _topics;
    private ICollection<Category> _childCategory;
    private ICollection<Media> _media;
    private Category _parentCategory;

    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
    public virtual bool IsActive { get; set; }

    public virtual Category ParentCategory
    {
        get { return _parentCategory ?? (_parentCategory = new Category()); }
        protected set { _parentCategory = value; }
    }

    public virtual ICollection<Category> ChildCategory
    {
        get { return _childCategory ?? (_childCategory = new List<Category>()); }
        protected set { _childCategory = value; }
    }


    public virtual void AddChild(Category childCategory)
    {
        childCategory.ParentCategory = this;
    }
}

Mapping

public class CategoryMapping : IAutoMappingOverride<Category>
{
    public void Override(AutoMapping<Category> mapping)
    {
        mapping.Map(c => c.Name).Length(30);
        mapping.Map(c => c.Description).Length(160);
        mapping.References(x => x.ParentCategory)
            .Cascade.None()
            .Column("ParentCategoryId");
        mapping.HasMany(x => x.ChildCategory)
            .Inverse().Cascade.All()
            .KeyColumn("ParentCategoryId");
    }
}

Test I am creating parent and child categories and trying to save all of them at once.

public void CanSaveAllNewObjectGraphFromCategory() {
    #region Categories and Child
    var categories = new sq.Category()
    {
        Description = "Category1",
        IsActive = true,
        Name = "Categoy1"
    };

    var childCat = new List<sq.Category>() {
        new sq.Category(){
            Description = "ChildCategory1",
            IsActive = true,
            Name = "CCategoy1"
        },
        new sq.Category(){
            Description = "ChildCategory2",
            IsActive = true,
            Name = "CCategoy2"
        }
    };

    foreach (var item in childCat)
    {
        categories.AddChild(item);
    }
    #endregion

    using (var tx = _session.BeginTransaction())
    {
        _catRepo.AddNewCategory(categories);
        tx.Commit(); // Error occurs when commit is executed.
    }
}

Solution

  • Your error message gave you two options:

    • Save the transient instance before flushing
    • Set cascade action for the property to something that would make it autosave

    The first option is straightforward: simply save each instance before commiting the transaction

    using (var tx = _session.BeginTransaction())
    {
      foreach(var category in categories)
      {
         _session.SaveOrUpdate(category);
      }
      _catRepo.AddNewCategory(categories);
      tx.Commit(); 
    }