EF6 dbContext SaveChanges:如何捕获回滚?

时间:2014-05-29 12:24:48

标签: .net entity-framework dbcontext

数据库在调用SaveChanges()后返回新记录的ID。每次我将一个新对象传递给我的Add方法时ID都会递增,所以我知道数据库(Azure)在循环中。但实际上没有任何内容插入到数据库中,因为存在回滚。除了执行.Find(newId)和测试null,这意味着再次访问数据库,有没有办法解决这个问题?

这是因为我无意中创建了一个需要插入相关表的关系。让我感到困惑的是,交易无声地失败了。如果我一直在使用ADO.NET和一个参数化的存储过程,它会抛出异常并返回一些有用的东西。

我是EF / Code First的新手并且正在开发一个绿地项目,因此可以首先使用代码构建数据库。到目前为止,这是一次冒险。

更新:根据Aron的请求提供代码。还有更多背景。我正在使用NLogMvc,它有自己的上下文来将消息写入数据库。 Aron提到的分布式事务可能存在问题。我在任何情况下重组了解决方案,删除了NLogMvc,并转移到了Ninject.Extensions.Loggings.nlog2

我知道数据库上发生的事情是我无法清理插入到相关表中所需的一些旧代码。 SQL Server会抛出一个错误回滚事务。

问题的最终更新:关于可能导致问题的所有假设都是错误的。事实证明,我在存储库构造函数中创建了一个TransactionScope实例,无意中搞砸了。有关详细信息,请参阅我对自己问题的回答(下文)。

代码:

        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;
        }
    }

1 个答案:

答案 0 :(得分:0)

最后通过构建一个按预期为其他实体工作的存储库来解决这个问题,然后对日志文件进行一些a-b比较。

在插入失败的情况下,在存储库构造函数中,我为私有成员变量创建了一个TransactionScope实例。我打算将事务用于其他事情,但是,正如您从我的代码中看到的那样,我确实将它用于失败的插入方法。

我没有使用插入方法在存储库的构造函数中创建TransactionScope实例。

我在这两笔交易中使用了新的EF6记录工具。请注意SQL EF发送到数据库的区别:

插入失败的内容: INSERT [dbo]。[Media_Slideshow]([Name],[Description],[DateAdded],[AddedBy],blah blah blah 2014年6月8日12:37:24 -05:00关闭连接 幻灯片ID为3

插入成功: 开始于6/8/2014 12:41:35 PM -05:00进行交易 INSERT [dbo]。[Blog]([Title],[BlogContent],[DateAdded],[AddedBy],blah,blah,blah 承诺交易时间为2014年6月8日下午12:41:35 -05:00 2014年6月8日下午12:41:35 -05:00关闭连接 博客ID为5

因此,由于我目前尚未理解且没有时间进行调查的原因,在存储库构造函数中实例化TransactionScope会导致插入失败。

如我的问题所述: 我试图插入的对象没有导航属性。 2.我没有(故意)对失败的方法使用TransactionScope。 3.我没有(故意)在成功的方法上使用TransactionScope。 4. SaveChanges()返回数据库为两个插入创建的id,但在第一种情况下,没有任何内容插入到数据库中。 5.没有其他背景可以开放。 6.失败的方法没有抛出异常;在控制器尝试使用新id检索记录之前,所有内容看起来都很好看。我能够诊断此问题的唯一方法是使用记录器。

通过查看日志可以清楚地看到,EF 确实创建了一个封面下的事务,在失败的插入的情况下,我通过实例化TransactionScope以某种方式搞砸了。