Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update only works in debug mode

I'm new to using entity as a data layer between MVC and SQL Server, so I apologize up front if what I'm doing is bad practice.

Let me start by sharing the code that is handling the update.

Update Delivery:

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

Let me continue by explaining that the delivery object has 2 children; DeliveryTime and DeliveryDay. The issue that arises happens when I try to remove one deliveryTime and modify nothing else. The end result of running the code normally (not in debug) is that the deliveryTime is in fact not removed. Here's the interesting thing guys, when I debug it and go through the break points, everything works as expected!

Let me continue by posting the code that is running behind the removal method of the deliveryTime (actually all entity objects in my system).

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

The code above is obviously generic, and it handles all my entity objects. I have tested it in and out and know for sure the problem does not lie in there. I thought it would be helpful to post it so you all can have a full understanding of what's going on.

Here's my best guess as to what's going on:

The reference to the removed deliveryTime still exists when the database context is saved, but when I debug, the system has enough time to remove the context.

Here was one of my attempted solutions:

Remove all references to the children objects immediately after setting currentDays and currentTimes and then proceeding to add them back to deliveryInDb as you enumerate through them.

Because I am new to all of this, if you see some bad practice along with the solution, I wouldn't mind constructive criticism to improve my programming method.

like image 203
Michael King Avatar asked Sep 07 '25 07:09

Michael King


1 Answers

I actually encountered this issue in a project at work. The project is an older MVC4 project using EF 6.1.

In our situation, a simple update attempting to set a related entity property to null was failing to actually set it to null while running the web app normally (in debug mode). When setting a break point on the line of code that sets the property to null the database would be updated as expected, though. So, the update was working when a break point was in place but not working when allowed to run normally.

Using an EF interceptor, we could see that, with the break point in place, the update query was going through as expected.

Now, in our situation the related entity was using the virtual keyword to allow for lazy loading. I think this is the root of the issue. When a break point is present, EF has enough time to both lazily load that related entity and evaluate whatever it needs to evaluate and finally set it to null. When running without a break point, I think EF gets caught up trying to lazily load that entity and therefore fails to think it needs to be updated. To be clear, I was both accessing the related entity property for the first time and setting it null using a one-liner of code.

foo.Bar = null;

I resolved this issue, in our scenario, by accessing that property at least once prior to setting it to null so that EF is forced to load it. With it loaded, setting it to null seems to work as intended now. So again, to be clear, I think the issue is a combo of lazy loading and the one-liner of code both accessing that property for the first time and assigning it to null.

like image 198
Ryan Avatar answered Sep 09 '25 05:09

Ryan