views:

39

answers:

2

I made a test class against the repository methods shown below:

        public void AddFile<TFileType>(TFileType FileToAdd) where TFileType : File
  {
   try
   {
    _session.Save(FileToAdd);
    _session.Flush();
   }
   catch (Exception e)
   {
    if (e.InnerException.Message.Contains("Violation of UNIQUE KEY"))
     throw new ArgumentException("Unique Name must be unique");
    else
     throw e;
   }
  }

        public void RemoveFile(File FileToRemove)
  {
   _session.Delete(FileToRemove);
   _session.Flush();
  }

And the test class:

            try
   {
    Data.File crashFile = new Data.File();
    crashFile.UniqueName = "NonUniqueFileNameTest";
    crashFile.Extension = ".abc";
    repo.AddFile(crashFile);
    Assert.Fail();
   }
   catch (Exception e)
   {
    Assert.IsInstanceOfType(e, typeof(ArgumentException));
   }

   // Clean up the file
   Data.File removeFile = repo.GetFiles().Where(f => f.UniqueName == "NonUniqueFileNameTest").FirstOrDefault();
   repo.RemoveFile(removeFile);

The test fails. When I step in to trace the problem, I found out that when I do the _session.flush() right after _session.delete(), it throws the exception, and if I look at the sql it does, it is actually submitting a "INSERT INTO" statement, which is exactly the sql that cause UNIQUE CONSTRAINT error. I tried to encapsulate both in transaction but still same problem happens. Anyone know the reason?


Edit

The other stay the same, only added Evict as suggested

        public void AddFile<TFileType>(TFileType FileToAdd) where TFileType : File
  {
   try
   {
    _session.Save(FileToAdd);
    _session.Flush();
   }
   catch (Exception e)
   {
   _session.Evict(FileToAdd);
    if (e.InnerException.Message.Contains("Violation of UNIQUE KEY"))
     throw new ArgumentException("Unique Name must be unique");
    else
     throw e;
   }
  }

No difference to the result.

A: 

NHibernate Manual "Best practices" Chapter 22:

This is more of a necessary practice than a "best" practice. When an exception occurs, roll back the ITransaction and close the ISession. If you don't, NHibernate can't guarantee that in-memory state accurately represents persistent state. As a special case of this, do not use ISession.Load() to determine if an instance with the given identifier exists on the database; use Get() or a query instead.

AJ
Nice call AJ. Problem here is, I suppose to use the same session thru out the whole request (init in beginrequest, remove in endrequest). So, if I close it, what should I do?
xandy
Nice one Jamie.
AJ
+1  A: 

Call _session.Evict(FileToAdd) in the catch block. Although the save fails, FileToAdd is still a transient object in the session and NH will attempt to persist (insert) it the next time the session is flushed.

Jamie Ide
I tried to evit the object in the catch as what you said. But it doesn't work, it still try to insert the problematic statement again in next run!
xandy
Please post the revised code. Do you call Evict before throwing the exception?
Jamie Ide
Code attached. Thx
xandy