2

Basically I have a Topic Object which can have multiple Category's associted with it. I'm trying to insert a new Category to a particular Topic and I get an error.

Working with ASP.NET 4 , also with windows-1255 encoding on the front-end and in ASP and hebrew_bin on MySQL (the back-end)

ISession session = dal.GetSession();
using (session.BeginTransaction())
{
    Topic t = session.Get<Topic>(topicId);
    Category c = new Category() { Name=name };
    t.AddCategory(c); // updates both references (inside `t` and inside `c`)
    session.Update(t);
    session.Save(c);
    session.Transaction.Commit();
}

Getting Error:

could not insert: [DAL.Models.Category#1][SQL: INSERT INTO Category (Name, Publish, TopicID, ID) VALUES (?, ?, ?, ?)]

Ultimately it fails because of a foreign key constraint violation (I guess ? is not a valid ID):

Cannot add or update a child row: a foreign key constraint fails (etladaatdb.category, CONSTRAINT FK6482F249B6E2851 FOREIGN KEY (ID) REFERENCES topic (ID))

Topic.cs:

public enum ColorEnum { Blue, Red, Green, Yellow }
    public class Topic
    {
        public virtual int ID { get; set; }
        public virtual string Name { get; set; }
        public virtual string Description { get; set; }
        public virtual string ImageUri { get; set; }
        public virtual ColorEnum Color { get; set; }

        public virtual IList<Category> Categories { get; set; }

        public virtual void AddCategory(Category c)
        {
            Categories.Add(c);
            c.Topic = this;
        }
    }

Topic.hbm.xml:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="DAL"
                   namespace="DAL.Models">
  <class name="Topic" lazy="true">
    <id name="ID">
      <generator class="increment"></generator>
    </id>
    <property name="Name" />
    <property name="Description" />
    <property name="ImageUri" />
    <property name="Color" type="ColorEnum" />

    <bag name="Categories" lazy="true" inverse="true"
                         batch-size="25" cascade="all-delete-orphan">
      <key column="ID" />
      <one-to-many class="Category" />
    </bag>
  </class>
</hibernate-mapping>

Category.cs:

public class Category
    {
        public virtual int ID { get; set; }
        public virtual string Name { get; set; }
        public virtual bool Publish { get; set; }

        public virtual Topic Topic { get; set; }
    }

Category.hbm.xml:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="DAL"
                   namespace="DAL.Models">
  <class name="Category" lazy="true">
    <id name="ID">
      <generator class="increment"></generator>
    </id>
    <property name="Name"/>
    <property name="Publish" />

    <many-to-one name="Topic" class="Topic" column="TopicID" />
  </class>
</hibernate-mapping>

2 Answers 2

1

It really seems, that you've just missed to assign topic to category. So this should do that trick:

// original
Category c = new Category() { Name=name };
t.AddCategory(c);
// always assign both sides during creation
c.Topic = t;

Now NHibernate will know what is the referenced Topic ... and will insert proper ID

About mapping. The relation many-to-one and one-to-many is expressed by exactly one column. It is the foreign key column in child (categories) table - TopicID.

So this is the correct mapping:

// Topic
<bag name="Categories" lazy="true" inverse="true"
                     batch-size="25" cascade="all-delete-orphan">
  // this is wrong
  // <key column="ID" />
  // this is the target column in the other table
  <key column="TopicID" />
  <one-to-many class="Category" />
</bag>

...

// Category
// the same columns for the same relation
<many-to-one name="Topic" class="Topic" column="TopicID" />

Having cascade in place, we can just save the topic:

session.Update(t);
// not needed, there is a cascade
// session.Save(c);
Sign up to request clarification or add additional context in comments.

3 Comments

Observed the mapping and found one issue. Relation is about one column for both sides...
Check my answer - the bag mapping would require <key column="TopicID" /> not the original <key column="ID" />
Amazing, enjoy mighty NHibernate, sir ;)
0

I think your entity is not complete and you should change your code. Do not forget to use a using statement for IDisposable objects.

using(ISession session = dal.GetSession())
{
using (session.BeginTransaction())
{
    Topic t = session.Get<Topic>(topicId);
    Category c = new Category() { Name=name };
    t.AddCategory(c);
    c.Topic = t;
    session.SaveOrUpdate(t);
    session.Transaction.Commit();
}
}

You could also inside your t.AddCategory(c); method:

public void AddCategory(Category category)
{
  // add to list
  category.Topic = this;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.