The database returns the ID of the new record after I call SaveChanges(). And the IDs increments each time I pass a new object into my Add method, so I know the database (Azure) is in the loop. But nothing is actually being inserted into the database because there was a rollback. Other than doing .Find(newId) and testing for null, which means another trip to the DB, is there any way to catch this?
This happened because I had inadvertently created a relationship which required an insert into a related table. What bugs me is that the transaction failed silently. If I had been using ADO.NET and a paramaterized stored procedure it would have thrown an exception and returned something useful.
I am new to EF / Code First and working on a greenfield project so have the luxury of building the database with code first. It's something of an adventure so far.
UPDATE: Providing code per Aron's request. Also, more background. I was using NLogMvc which has its own context to write messages to the database. There may have been an issue there with the distributed transaction Aron mentioned. I have restructured the solution in any case, removed NLogMvc, and shifted to Ninject.Extensions.Loggings.nlog2
I know that what was happening on the database was that I had failed to clean up some old code that required in insert into a related table. SQL Server would have thrown an error rolled back the transaction.
FINAL UPDATE TO THE QUESTION: All my assumptions as to what might be causing the problem were wrong. Turns out I inadvertently screwed up by creating an instance of TransactionScope in the repository constructor. See my answer to my own question (below) for the details.
The code:
public int AddUpdateSlideshow(SlideshowDto slideshow)
{
if (!_user.IsInRole("Admin")) return -1;
bool isNewShow = false;
var now = DateTime.Now;
try
{
using (var context = new ApplicationDbContext())
{
context.Database.Log = s => _logger.Info(s);
int slideshowId;
if (slideshow.SlideshowId == 0)
{
isNewShow = true;
var newSlideshow = new Slideshow
{
Name = slideshow.Name,
Description = slideshow.Description,
DateAdded = now,
AddedBy = _userName,
DateLastUpdated = now,
UpdatedBy = _userName,
IsActive = slideshow.IsActive
};
context.Slideshows.Add(newSlideshow);
context.SaveChanges();
slideshowId = newSlideshow.SlideshowId;
}
else
{
Slideshow dbUpdate =
context.Slideshows.Find(
slideshow.SlideshowId);
if (dbUpdate != null)
{
slideshowId = slideshow.SlideshowId;
dbUpdate.Name = slideshow.Name;
dbUpdate.Description = slideshow.Description;
dbUpdate.IsActive = slideshow.IsActive;
dbUpdate.DateLastUpdated = now;
dbUpdate.UpdatedBy = _userName;
context.SaveChanges();
// Number of objects written to the database.
// Zero means the update failed.
int result = context.SaveChanges();
slideshowId = result == 0 ? result : slideshow.SlideshowId;
}
else
{
var errorMessage = string.Format(
"The {0} method in {1} threw an ArgumentNullException exception. The user was {2}.",
MethodBase.GetCurrentMethod().Name, _className, _userName);
_logger.Error(errorMessage);
throw new ArgumentNullException("slideshow");
}
}
// The database will return new id value BUT
// if for some reason the transaction fails the
// changes will be rolled back and we won't have the
// new record. So we need to confirm that we do in
// fact have one.
if (isNewShow)
{
var newShow = context.Slideshows.Find(slideshowId);
if (newShow == null)
{
slideshowId = 0;
}
}
return slideshowId;
}
}
catch (Exception ex)
{
var errorMessage = string.Format("The {0} method in {1} threw an exception. The user was {2}.",
MethodBase.GetCurrentMethod().Name, _className, _userName);
_logger.Error(errorMessage, ex);
return -1;
}
}