NHibernate - NonUniqueObjectException

272 views Asked by At

The TL;DR version is this: I keep getting a NonUniqueObjectException when trying to delete an entity in a unit test. Can anyone tell me how NHibernate checks the cache and compares objects if it is not using the Equals methods or GetHashCode?

I have an application I am trying to test using the following code.

[Test]
public void _04_Category_Delete_Test() {
   using(var lifetime = container.BeginLifetimeScope()) {
       ICategoryRepository categoryRepository = lifetime.Resolve<ICategoryRepository>();
       DefaultCommandBus commandBus = lifetime.Resolve<DefaultCommandBus>();
       IMappingEngine mapper = lifetime.Resolve<IMappingEngine>();

       Category category = categoryRepository.Get(x => x.Title == "Category Descriptions Blah blah");
       Assert.IsNotNull(category, "Error: Category was not found.");

       DeleteCategoryCommand command = mapper.Map<DeleteCategoryCommand>(category);

       ICommandHandler<DeleteCategoryCommand> commandHandler = lifetime.Resolve<ICommandHandler<DeleteCategoryCommand>>();
       ICommandResult result = commandBus.Submit(command, commandHandler);

       Assert.IsNotNull(result, "Error: Category was not deleted by command bus");
       Assert.IsTrue(result.Success, "Error: Category was not deleted by command bus.");
   }
}

As you can see, the category entity is mapped to a command object and passed off to a handler through a command bus. Within the handler, the command object is then converted back to an entity. The mapping profile for this conversion ignores all of the properties with the exception of the Id.

The generic repository for the delete is:

    public bool Delete(T entity) {
        this.session.Delete(entity);
        return true;
    }

When this method executes, I get a NonUniqueObjectException. I have an generic EntityBase, from which my entities inherit, and which overrides the Equals and GetHashCode methods. While in the Debugger, I can test the equality of the entity passed into the Delete method and the entity in NHibernate's session cache and they evaluate to true. When the Delete method executes on the session object, it does not execute any of the overridden Equals or GetHashCode methods.

Can anyone tell me how NHibernate checks the cache and compares objects if it is not using the Equals methods or GetHashCode? I revised my code to the following, but it feels like a hack.

    public bool Delete(T entity) {
        var id = entity.GetType().GetProperty("Id").GetValue(entity, null);
        var sessionEntity = this.session.Load<T>(id);

        var sessionEntityHashCode = sessionEntity.GetHashCode();
        var entityHashCode = entity.GetHashCode();

        if (!ReferenceEquals(null, sessionEntity)) {
            this.session.Delete(sessionEntity);
        } else {
            this.session.Delete(entity);
        }

        return true;
    }
1

There are 1 answers

0
DerHaifisch On BEST ANSWER

It turns out that it was a scoping issue. I had to change the code to the following to the following:

Category category = null; 

using(var lifetime = container.BeginLifetimeScope()) {
    ICategoryRepository categoryRepository = lifetime.Resolve<ICategoryRepository>();
    DefaultCommandBus commandBus = lifetime.Resolve<DefaultCommandBus>();
    IMappingEngine mapper = lifetime.Resolve<IMappingEngine>();

    category = categoryRepository.Get(x => x.Title == "Updated Category Title");
    Assert.IsNotNull(category, "Error: Category was not found.");
}

using(var lifetime = container.BeginLifetimeScope()) {
    DefaultCommandBus commandBus = lifetime.Resolve<DefaultCommandBus>();
    IMappingEngine mapper = lifetime.Resolve<IMappingEngine>();

    DeleteCategoryCommand command = mapper.Map<DeleteCategoryCommand>(category);

    ICommandHandler<DeleteCategoryCommand> commandHandler = lifetime.Resolve<ICommandHandler<DeleteCategoryCommand>>();
    ICommandResult result = commandBus.Submit(command, commandHandler);

    Assert.IsNotNull(result, "Error: Category was not deleted by command bus");
    Assert.IsTrue(result.Success, "Error: Category was not deleted by command bus.");
}