【问题标题】:Update only works in debug mode更新仅适用于调试模式
【发布时间】:2018-03-02 23:20:54
【问题描述】:

我不熟悉将实体用作 MVC 和 SQL Server 之间的数据层,所以如果我的做法是不好的做法,我先道歉。

首先让我分享处理更新的代码。

更新交付:

public bool One(Delivery toUpdate)
{
    using (var dbContext = new FDb())
    {
        try
        {
            var deliveryInDb = this.dbTable(dbContext).Single(x => x.DeliveryId == toUpdate.DeliveryId);

            dbContext.Entry(deliveryInDb).CurrentValues.SetValues(toUpdate);

            //removal first
            List<DeliveryDay> currentDays = FEngineCore.DeliveryDay.Get.ForValue((x => x.DeliveryId), toUpdate.DeliveryId);
            List<DeliveryTime> currentTimes = FEngineCore.DeliveryTime.Get.ForValue((x => x.DeliveryId), toUpdate.DeliveryId);

            //remove delivery days that are not needed
            foreach (var curDay in currentDays)
            {
                if (!toUpdate.DeliveryDays.Select(x => x.DeliveryDayId).Contains(curDay.DeliveryDayId))
                {
                    FEngineCore.DeliveryDay.Delete.One((x => x.DeliveryDayId), curDay.DeliveryDayId);
                    deliveryInDb.DeliveryDays.Remove(curDay);
                }
            }

            //remove delivery times that are not needed
            foreach (var curTime in currentTimes)
            {
                if (!toUpdate.DeliveryTimes.Select(x => x.DeliveryTimeId).Contains(curTime.DeliveryTimeId))
                {
                    FEngineCore.DeliveryTime.Delete.One((x => x.DeliveryTimeId), curTime.DeliveryTimeId);
                    deliveryInDb.DeliveryTimes.Remove(curTime);
                }
            }

            foreach (var day in toUpdate.DeliveryDays)
            {
                if (day.DeliveryDayId == 0)
                {
                    dbContext.DeliveryDays.Add(day);
                }
                else
                {
                    if (dbContext.DeliveryDays.Local.Any(e => e.DeliveryDayId == day.DeliveryDayId))
                    {
                        dbContext.Entry(dbContext.DeliveryDays.Local.First(e => e.DeliveryDayId == day.DeliveryDayId)).CurrentValues.SetValues(day);
                        dbContext.Entry(dbContext.DeliveryDays.Local.First(e => e.DeliveryDayId == day.DeliveryDayId)).State = EntityState.Modified;
                    }
                    else
                    {
                        DeliveryDay modDay = new DeliveryDay
                        {
                            DayOfWeek = day.DayOfWeek,
                            DeliveryDayId = day.DeliveryDayId,
                            DeliveryId = day.DeliveryId,
                            Interval = day.Interval
                        };

                        dbContext.DeliveryDays.Attach(modDay);
                        dbContext.Entry(modDay).State = EntityState.Modified;
                    }

                    deliveryInDb.DeliveryDays.Add(day);
                }
            }

            foreach (var time in toUpdate.DeliveryTimes)
            {
                if (time.DeliveryTimeId == 0)
                {
                    dbContext.DeliveryTimes.Add(time);
                }
                else
                {
                    if (dbContext.DeliveryTimes.Local.Any(e => e.DeliveryTimeId == time.DeliveryTimeId))
                    {
                        dbContext.Entry(dbContext.DeliveryTimes.Local.First(e => e.DeliveryTimeId == time.DeliveryTimeId)).CurrentValues.SetValues(time);
                        dbContext.Entry(dbContext.DeliveryTimes.Local.First(e => e.DeliveryTimeId == time.DeliveryTimeId)).State = EntityState.Modified;
                    }
                    else
                    {
                        DeliveryTime modTime = new DeliveryTime
                        {
                            DeliveryId = time.DeliveryId,
                            DeliveryLocationId = time.DeliveryLocationId,
                            DeliveryTimeId = time.DeliveryTimeId,
                            DropoffTime = time.DropoffTime
                        };

                        dbContext.DeliveryTimes.Attach(modTime);
                        dbContext.Entry(modTime).State = EntityState.Modified;
                    }

                    deliveryInDb.DeliveryTimes.Add(time);
                }
            }

            dbContext.SaveChanges();
            dbContext.Entry(deliveryInDb).State = EntityState.Detached;

            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.InnerException);
            return false;
        }
    }
}

让我继续解释交付对象有 2 个孩子; DeliveryTimeDeliveryDay。当我尝试删除一个 deliveryTime 并且不修改任何其他内容时,就会出现问题。正常运行代码(不在调试中)的最终结果是 deliveryTime 实际上没有 被删除。这是有趣的事情,当我调试它并通过断点时,一切都按预期工作!

让我继续发布在 deliveryTime 的删除方法后面运行的代码(实际上是我系统中的所有实体对象)。

public bool One<V>(Expression<Func<T, V>> property, V value) where V : IComparable
{
    using (var dbContext = new FoodsbyDb())
    {
        try
        {
            T toDelete; 

            //get the body as a property that represents the property of the entity object
            MemberExpression entityPropertyExpression = property.Body as MemberExpression;

            //get the parameter that is representing the entity object
            ParameterExpression entityObjectExpression = (ParameterExpression)entityPropertyExpression.Expression;

            //represent the value being checked against as an expression constant
            Expression valueAsExpression = Expression.Constant(value);

            //check the equality of the property and the value
            Expression equalsExpression = Expression.Equal(entityPropertyExpression, valueAsExpression);

            //create an expression that takes the entity object as a parameter, and checks the equality using the equalsExpression variable
            Expression<Func<T, bool>> filterLambda = Expression.Lambda<Func<T, bool>>(equalsExpression, entityObjectExpression);

            toDelete = this.dbTable(dbContext)
                .SingleOrDefault(filterLambda);

            if (toDelete != null)
            {
                this.dbTable(dbContext)
                .Remove(toDelete);

                dbContext.SaveChanges();

                return true;
            }

            return false;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.InnerException);
            return false;
        }
    }
}

上面的代码显然是通用的,它处理了我所有的实体对象。我已经对其进行了内外测试,并且确定问题不在于那里。我认为发布它会有所帮助,这样大家就可以全面了解正在发生的事情。

这是我对正在发生的事情的最佳猜测:

在保存数据库上下文时,对删除的deliveryTime的引用仍然存在,但是当我调试时,系统有足够的时间来删除上下文。

这是我尝试的解决方案之一:

在设置 currentDayscurrentTimes 之后立即 删除对子对象的所有引用,然后继续将它们添加回 deliveryInDb 当你枚举它们时。

因为我对这一切都不熟悉,如果您在解决方案中看到一些不好的做法,我不介意提出建设性的批评来改进我的编程方法。

【问题讨论】:

  • 您的代码太复杂,一眼就能理解,但我立即注意到的一件事是您正在使用 DbContext 的多个实例。如果它们不同步,那将给您带来问题。例如,您可能在一个上下文中的缓存中有一条记录,但随后您在另一个上下文中将其删除......第一个上下文不会知道这一点。
  • @ErikFunkenbusch 哦,天哪。就在那里编程101,我怎么可能没想到。我将通过上下文并提供最新情况。
  • 您还需要考虑您的工作流程,以及何时何地调用 SaveChanges。您不想在某个部分删除对象,保存更改,然后发现您不想删除并回滚它(除非您正在实现一个完整的包含事务,这可能没有良好的性能)。 EF 是一个工作单元,重点是您构建一系列操作,然后一次性提交所有操作。
  • 好电话@ErikFunkenbusch。如果没有通过上下文,我只会在删除时保存更改。感谢您提供详细而智能的输入,这真的很有帮助。
  • @ErikFunkenbusch 我知道这不在问题的范围内,但是您在第一条评论中提到我的代码太复杂而无法一目了然。这是不良做法的结果吗?我应该重新格式化逻辑以使其不那么复杂吗?

标签: c# sql-server entity-framework


【解决方案1】:

我实际上在工作中的一个项目中遇到了这个问题。该项目是使用 EF 6.1 的旧 MVC4 项目。

在我们的情况下,尝试将相关实体属性设置为 null 的简单更新在正常运行 Web 应用程序(在调试模式下)时未能将其实际设置为 null。但是,在将属性设置为 null 的代码行上设置断点时,数据库将按预期更新。因此,当断点到位时更新工作正常,但允许正常运行时更新不工作。

使用 EF 拦截器,我们可以看到,在断点到位后,更新查询按预期进行。

现在,在我们的情况下,相关实体使用virtual 关键字来允许延迟加载。我认为这是问题的根源。当存在断点时,EF 有足够的时间来延迟加载该相关实体并评估它需要评估的任何内容,最后将其设置为 null。在没有断点的情况下运行时,我认为 EF 会试图延迟加载该实体,因此无法认为它需要更新。需要明确的是,我是第一次访问相关实体属性使用单行代码将其设置为 null。

foo.Bar = null;

在我们的场景中,我解决了这个问题,方法是在将其设置为 null 之前至少访问该属性一次,以便强制 EF 加载它。加载后,将其设置为 null 似乎现在可以按预期工作。因此,再次明确地说,我认为问题是延迟加载和单行代码的组合,它们都是第一次访问该属性并将其分配给 null。

【讨论】:

  • 哇,谢谢 Ryan,我刚刚遇到了完全相同的问题,我可以按照您的建议解决它,方法是先阅读 foo.Bar,以便正确发出对数据库的请求(var myWorkaround = foo .Bar) 然后执行 foo.Bar = null 而不是 context.SaveChanges();
  • 偶然发现了同样的问题。一直在不懈地尝试修复它但没有成功,希望就是这样。
【解决方案2】:

您似乎正在使用多个未同步的 DbContext 实例。

解决方案是使用单个实例,并在您的方法之间传递该实例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-31
    • 2014-09-16
    • 1970-01-01
    • 2018-12-25
    • 2014-12-19
    • 1970-01-01
    • 2018-04-20
    • 1970-01-01
    相关资源
    最近更新 更多