EntityFramework 更新在 SaveChanges / ASP.NET MVC / postbacks 上随机失败
Posted
技术标签:
【中文标题】EntityFramework 更新在 SaveChanges / ASP.NET MVC / postbacks 上随机失败【英文标题】:EntityFramework update fails randomly on SaveChanges / ASP.NET MVC / postbacks 【发布时间】:2020-01-26 11:40:51 【问题描述】:我正在通过 MVC 中的回发进行实体更新:
ControlChartPeriod period = _controlChartPeriodService.Get(request.PeriodId);
if (period != null)
SerializableStringDictionary dict = new SerializableStringDictionary();
dict.Add("lambda", request.Lambda.ToString());
dict.Add("h", request.h.ToString());
dict.Add("k", request.k.ToString());
period.Parameters = dict.ToXmlString();
// ToDo : fails on save every now and then when one of the values changes; fails on Foreign Key being null
try
_controlChartPeriodService.Update(period, request.PeriodId);
return Ok(request);
更新方法如下:
public TObject Update(TObject updated, TKey key)
if (updated == null)
return null;
TObject existing = _context.Set<TObject>().Find(key);
if (existing != null)
_context.Entry(existing).CurrentValues.SetValues(updated);
_context.SaveChanges();
return existing;
public TObject Get(TKey id)
return _context.Set<TObject>().Find(id);
奇怪的是我第一次运行它时它通常运行良好;如果我进行第二次回发,它不起作用并且因外键的 EntityValidation 错误而失败;但是检查实体外键看起来很好并且没有受到影响。
我是否需要在某处同步上下文?
我一直在尝试找出成功与失败的区别。
我正在为存储库使用注入:
public void ConfigureServices(IServiceCollection services)
services.AddScoped((_) => new DataContext(ConfigSettings.Database.ConnectionString));
services.AddScoped<ControlChartPeriodService, ControlChartPeriodService>();
--更新:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id get; set;
public DateTime StartDate get; set;
public DateTime? EndDate get; set;
public virtual ICollection<ControlChartPoint> ControlChartPoints get; set;
[Required]
public virtual ControlChart ControlChart get; set;
public string Parameters get; set;
在 ControlChartMap 中,我们有以下内容:
HasMany(x => x.Periods)
.WithRequired(c => c.ControlChart);
【问题讨论】:
与问题无关,但我看到您将ControlChartPeriodService
添加到IServiceCollection
作为实现和接口。应该是AddScoped<IControlChartPeriodService, ControlChartPeriodService>()
谢谢,我已经以这种方式添加了所有存储库,因为我没有为它们创建接口。
“EntityValidation error of a foreign key”是什么意思?
表示周期的导航属性ControlChart不能为空;但是它不为空,因为它是作为实体动态加载的。
听起来是数据库上下文范围的问题
【参考方案1】:
如果您期望 TObject 的加载也包括关系,在我看来,您的问题出在这一行:
TObject existing = _context.Set<TObject>().Find(key);
Find 只会加载 TObject 记录,但不会加载它的关系,因为代码中没有明确引用 ControlChart 属性它不会延迟加载 - 这似乎是您所期望的行为,所以我假设您启用了延迟加载
关系已经被配置为的事实
HasMany(x => x.Periods).WithRequired(c => c.ControlChart);
表示TObject的ControlChart属性是一组相关的Periods,但这并不意味着它会强制加载 TObject 时关系的延迟加载。
(我个人尽量避免由于这样的事情而依赖延迟加载,并且更喜欢实际禁用它并通过 Include 急切地加载关系,或者以较少的措施,显式加载,使用 Load。这可能是个人喜好问题,但我发现这种方式可以让你免于许多麻烦)
因此,如果您启用了延迟加载并且喜欢它的工作方式,请在加载和保存之前的任何时间点引用 ControlChart 属性,例如
var chart= existing.ControlChart;
或类似的东西
或者对于非延迟加载的场景,虽然它可以在任何情况下使用:
TObject existing = _context.Set<TObject>().Where(x=>x.Id==key).Include(x=> x.ControlChart).FirstOrDefault();
【讨论】:
我现在已经简单地加载了 ControlChart 对象。这确实解决了问题;但我仍然不确定如何。我查看了这些对象,延迟加载的 DynamicProxy 部分似乎没有变化,所以肯定有一些我看不到的东西,现在很好。稍后我将更新我的数据层,以便对 EF 进行更深入的查询,减少对 LazyLoad 的依赖【参考方案2】:您是否查看过您尝试注入的不同对象图。通用方法可以在第一次通过时处理,但在更新时可能会失败。也仅仅是因为使用的结果 SQL 或 Datastore 引擎语言在插入和更新时不会产生相同的结果......
我经验。这个,在我的例子中,它来自后面的模型。查看 Foreign Ks 约束等...有些可能没有正确创建,因此更新失败...
【讨论】:
【参考方案3】:这只是暗中的一击,但是......
根据我的经验,如果 Entity Framework 随机工作/不工作,那是因为我需要使用 async/await
来确保在方法开始工作之前成功收集数据。
这或许可以解释为什么您会收到空外键错误。如果该方法超前,它将尝试处理一个新的 TObject,而不是您要从数据库中收集的那个。
尝试将其转换为异步函数,并将所有 get/set 方法转换为它们的异步版本。
即
public async Task<TObject> Update(TObject updated, TKey key)
if (updated == null)
return null;
TObject existing = await _context.Set<TObject>().FindAsync(key);
if (existing != null)
_context.Entry(existing).CurrentValues.SetValues(updated);
_ = await_context.SaveChangesAsync();
return existing;
然后在main方法中(也应该是异步的)
try
_ = await _controlChartPeriodService.Update(period, request.PeriodId);
return Ok(request);
【讨论】:
以上是关于EntityFramework 更新在 SaveChanges / ASP.NET MVC / postbacks 上随机失败的主要内容,如果未能解决你的问题,请参考以下文章
EntityFramework 更新在 SaveChanges / ASP.NET MVC / postbacks 上随机失败
EntityFramework CodeFirst 数据库不更新