4

This is driving me crazy. I can get one of the collections to save on a class, but not the other.

I have a class called Category

public Category()
{
    Items = new List<Item>();
    Prices = new List<Price>();
}

That has these two methods that are pretty much identical. The constructors set a property called Category and their Name and Price respectively.

public virtual Item AddItem(string name)
{
    var item = new Item(this, name);
    Items.Add(item);
    return item;
}

public virtual Price AddPrice(decimal price)
{
    var price = new Price(this, price);
    Prices.Add(price);
    return price;
}

Mappings

public class CategoryMap : ClassMap<Category>
{
    public CategoryMap()
    {
       // Other properties

       HasMany(x=>x.Items).Cascade.AllDeleteOrphan();
       HasMany(x=>x.Prices).Cascade.AllDeleteOrphan();
    }
}

As you can see, the two collections are mapped the exact same way.

The maps for Price and Item both have the same mapping

References(x=>x.Category);

As far as I can tell, almost everything about these two are identical. Here's the problem

category.AddItem(someName);
session.Save(category);    // Works

category.AddPrice(somePrice);
session.Save(category);    // Doesn't work

These are in a file I use to populate a test database. Saving the Item works fine. Saving the Price does not. The classes and the mappings are identical. I can't figure it out.

The mappings should work fine as calling the constructor directly works fine

session.Save(new Price(category, 1));

So why does Category save one collection and not the other? I ran profiler, and the class isn't even trying to save the Price collection.

Update:

As Gabe points out in the comment, if I swap the calls so that Price is called before Item, the Price goes into the database and Item does not.

If I call session.Flush() after every collection update, it works fine. Am I supposed to have to do this, or is there a way to fix my mapping so that it works?

Brandon
  • 68,708
  • 30
  • 194
  • 223
  • 2
    If you swap the order of the calls (add price first, then Item) do you get the same behavior? – Gabe Moothart Mar 05 '12 at 17:16
  • @GabeMoothart, well that's surprising. Looks like only the first call is saving. If I swap them, the price saves and the item doesn't. I tried calling `session.Flush();` after the call, but then I get the error `Cannot update identity column CategoryId` – Brandon Mar 05 '12 at 17:27
  • @GabeMoothart, nevermind. That error was related to another mapping issue. Could you post your comment as an answer? session.flush works, but would you be able to tell me if I'm supposed to be calling it after every collection update, or is there a way to fix my mapping so I don't have to? – Brandon Mar 05 '12 at 17:30

1 Answers1

1

Try swapping the order of the calls (add price first, then item) to see if it is really a problem with the Price mapping, or if the problem is that the second call fails.

Regarding flush - most of the time you don't need to call flush explicitly, it is something that nHibernate will do in the background when it needs to (details here). Flush mode is not a mapping issue (it is a session property that you can set, but again usually you don't have to).

If you are wrapping your code in a transaction, flush is not required. If you aren't, you should be and that might be your problem.

using (var tx = Session.BeginTransaction()) {

    category.AddItem(someName);
    category.AddPrice(somePrice);
    session.Save(category);

    tx.Commit();
}

update: I think I know what your problem was. Given your code, it looks like you're creating a new category object and populating it with data. On the first Save(), nHibernate does an auto-flush because it can't assign a category_id to the child collection until it gets one from the db. But the second time around, category has an id and so the session doesn't need to be flushed immediately. nHibernate is not chatty with the database when it doesn't need to be. The solution, as I mentioned above, is to always use transactions to specify when you're done doing your thing.

Community
  • 1
  • 1
Gabe Moothart
  • 31,211
  • 14
  • 77
  • 99
  • Thanks a lot Gabe. I'll look at refactoring my code into a transaction, but for now calling Flush explicitly works. – Brandon Mar 05 '12 at 18:37
  • If category is a persistent object (retrieved from session) you don't even need to call `session.Save`. You can just commit the transaction. – Cole W Mar 06 '12 at 23:30