0

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.
    }
}
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
Charanraj Golla
  • 1,102
  • 3
  • 17
  • 35

2 Answers2

1

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(); 
}
Andrew Shepherd
  • 44,254
  • 30
  • 139
  • 205
0

We would need to to assign both sides of references

public virtual void AddChild(Category childCategory)
{
    childCategory.ParentCategory = this;
    // add to collection as well
   _childCategory.Add(childCategory);
}
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335