【问题标题】:EntityFramework update fails randomly on SaveChanges / ASP.NET MVC / postbacksEntityFramework 更新在 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不能为空;但是它不为空,因为它是作为实体动态加载的。
  • 听起来是数据库上下文范围的问题

标签: c# entity-framework model-view-controller


【解决方案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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-31
      • 1970-01-01
      • 1970-01-01
      • 2010-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多