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&lt;IControlChartPeriodService, ControlChartPeriodService&gt;() 谢谢,我已经以这种方式添加了所有存储库,因为我没有为它们创建接口。 “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);

表示TObjectControlChart属性是一组相关的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数据更新之技巧

EntityFramework 更新在 SaveChanges / ASP.NET MVC / postbacks 上随机失败

EntityFramework CodeFirst 数据库不更新

EntityFramework 更新实体失败 - 无法跟踪

如何使用 AutoMapper 使用 EntityFramework 使用嵌套列表更新对象?

EntityFramework 更新 6.0 到 6.1.3 后仍然没有异步方法?