1

I have a list of Item entities that are being processed in a batch, something like this:

foreach (var itemId in ItemIdList)
{
    var item = getById(itemId); //load line
    if(item != null)
    {
      //...do some processing 
      delete(item)
    }
}

The problem is, the same itemId could be listed in ItemIdList multiple times, so after it is deleted, and I try to load the item a second time, the load line fails with the error

Unexpected row count: 0; expected: 1  (stale state exception)

I understand that the entity is not there any more, but I would have expected my get function to just return null. Here is my getById function:

var item = (from i in UnitOfWork.CurrentSession.QueryOver<T>()
                        where i.Id == id
                        select i
                        ).SingleOrDefault();

Why isn't SingleOrDefault just returning null?

My Item entity only has one autogenerated key and the hash function looks like this:

public override int GetHashCode()
    {
        int hashCode = 0;

        hashCode = hashCode ^ Id.GetHashCode();

        return hashCode;
    }

Edit:

Here is my delete method

    public void Delete(T t) //T would be Item in this case
    {
       UnitOfWork.CurrentSession.Delete(t);
    }
getit
  • 623
  • 2
  • 8
  • 30

1 Answers1

2

I haven't seen your delete method but since you state these items are being done in a 'batch' I'll assume you wait to flush until everything has been deleted.

Since the flush doesn't occur until you've attempted to delete all the items, it still exists in the database. Therefore, when you retrieve the item a second time it thinks that it should be 'deleted' but, behold, it still exists. This is why NHibernate thinks that the state is 'stale.'

One easy way to fix this would be to have the ItemIdList be a set (like a HashSet). This will prevent duplicate IDs from being present and should fix the problem.

On another note, if you are attempting to delete all entities within a list of ids, there are a lot more efficient ways than reading each one, one at a time, from the database and then deleting them.

docmanhattan
  • 2,368
  • 1
  • 25
  • 28
  • Commit gets called at the end of the request, I'm not sure if that is what you mean by flushing. But I understand what you mean that the Item would still be there since I have not commited. Although, I did get around my problem by first trying to load the item before deleting it. I didn't expect that to work but it did (the load function could not find it since it was deleted, but no commit yet...). Also, is this the kind of bulk processing you were thinking of: http://stackoverflow.com/questions/2293014/how-to-bulk-delete-with-nhibernate? – getit Aug 17 '12 at 22:23
  • Yes, 'commit' will 'flush' the session and, yes, that link is what I was thinking to do. There is no need to go to the database to retrieve the items for the sole purpose of deleting them. Not only that, but you are doing it one at a time so you would access the DB (n + 1) times, where n is the number of items you are deleting. The way in the link would be more efficient by far. Frankly, having to manually delete the items seems like you haven't designed/mapped things properly for NHibernate but I'm not sure of your setup so it could be perfectly legitimate. – docmanhattan Aug 17 '12 at 22:52