我是新手使用实体作为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个孩子; DeliveryTime和DeliveryDay。当我尝试删除一个deliveryTime并且不修改任何其他内容时,会出现问题。正常运行代码(不在调试中)的最终结果是,deliveryTime实际上并未删除。这是有趣的事情,当我调试它并通过断点时,一切都按预期工作!
让我继续发布在deliveryTime的remove方法后面运行的代码(实际上是我系统中的所有实体对象)。
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的引用仍然存在,但是当我调试时,系统有足够的时间来删除上下文。
这是我尝试过的解决方案之一:
在设置currentDays和currentTimes之后立即删除对children对象的所有引用,然后在枚举时继续将它们添加回deliveryInDb。
因为我是所有这一切的新手,如果你看到一些不好的做法和解决方案,我不介意建设性的批评来改进我的编程方法。
您似乎正在使用DbContext的多个实例,这些实例未同步。
解决方案是使用单个实例,并在您的方法之间传递该实例。
我实际上在工作中遇到了这个问题。该项目是使用EF 6.1的较旧的MVC4项目。
在我们的情况下,尝试将相关实体属性设置为null
的简单更新未能在正常运行Web应用程序时(在调试模式下)将其实际设置为null。在将属性设置为null的代码行上设置断点时,数据库将按预期更新。因此,更新在断点到位时正在工作,但在允许正常运行时不起作用。
使用EF拦截器,我们可以看到,在断点到位的情况下,更新查询按预期进行。
现在,在我们的情况下,相关实体使用virtual
关键字来允许延迟加载。我认为这是问题的根源。当存在断点时,EF有足够的时间来懒惰地加载相关实体并评估它需要评估的任何内容并最终将其设置为null。当没有断点运行时,我认为EF会陷入困境,试图懒洋洋地加载该实体,因此无法认为它需要更新。为了清楚起见,我第一次访问相关的实体属性,并使用一行代码将其设置为null。
foo.Bar = null;
在我们的场景中,我通过在将其设置为null之前至少访问该属性一次来解决此问题,以便强制EF加载它。加载它后,将其设置为null似乎现在按预期工作。再说一次,要清楚,我认为问题是延迟加载的组合和第一次访问该属性并将其赋值为null的一行代码。