【问题标题】:DbContext.SaveChanges() not updating database after modifying the entity修改实体后 DbContext.SaveChanges() 不更新数据库
【发布时间】:2019-08-22 09:32:52
【问题描述】:

我正在使用实体框架从数据库中读取一些数据(读取成功)并且修改实体工作正常,但它不会将更改保存到数据库中。

该代码读取了 15 条记录,但并未在循环内遍历所有记录。它遍历列表的第一个元素并在 DbContext.SaveChanges() 处停止而不保存更改。

知道我的代码有什么问题吗?

public async Task LinkCardAsync(postcardContext DbContext)
        {
            HttpClient cons = new HttpClient();
            cons.BaseAddress = new Uri("http://cbsswfint01:53303/");
            cons.DefaultRequestHeaders.Accept.Clear();
            cons.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
            using (cons)
            {
                try
                {
                    var cardsData = DbContext.MasterpassBulkLoad.Take(15).ToList();
                    foreach (var cardDetails in cardsData)
                    {
                        Console.WriteLine(cardDetails.Msisdn);
                        Console.WriteLine(cardDetails.Status == null);
                        HttpResponseMessage res = await cons.PostAsJsonAsync("api/Cardmanagement/CardLoad", cardDetails);
                        cardDetails.Status = (int)res.StatusCode;
                        Console.WriteLine("before save");
                        DbContext.SaveChanges();
                        Console.WriteLine("After Save");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.InnerException.Message);
                    _logger.LogError(ex.InnerException.StackTrace);
                }
            }
        }
public void Run()
        {
            using (var DbContext = new postcardContext())
            {
                _cardLinkService.LinkCardAsync(DbContext);
            }
            Console.ReadKey();


        }

【问题讨论】:

  • 任何错误或异常?
  • 没有错误也没有异常。它只是在第一次迭代时停止而不保存更改。
  • 在 foreach 循环外调用 DbContext.SaveChanges(),如果仍未保存,请尝试 for 循环并使用 cardsData.Get(index).Status = ... 更新 Status
  • @GiddyNaya 我刚试过。代码仍然做同样的事情
  • @GiddyNaya,在循环外使用 DbContext.SaveChanges(),它会迭代我们的列表,但不会将更改保存到数据库

标签: c# database entity-framework .net-core


【解决方案1】:

首先,您的调用代码应该正在等待卡链接服务的调用。

但是,我认为问题的症结在于 DbContext 不是线程安全的,因此您应该避免在与非上下文相关的等待异步操作之后访问上下文。 (PostAsJsonAsync) 根据同步上下文,等待的代码可以在不同的线程上恢复。

例如,其中 [#] 表示您将拥有的线程 ID:

      [1]  using (var DbContext = new postcardContext())
      [1]  {
      [1]      _cardLinkService.LinkCardAsync(DbContext);

-->(进入方法)

       ...
      [1]          var cardsData = DbContext.MasterpassBulkLoad.Take(15).ToList();
      [1]          foreach (var cardDetails in cardsData)
      [1]          {
      [1]              Console.WriteLine(cardDetails.Msisdn);
      [1]              Console.WriteLine(cardDetails.Status == null);
      [1]              HttpResponseMessage res = await cons.PostAsJsonAsync("api/Cardmanagement/CardLoad", cardDetails);
      [2]              cardDetails.Status = (int)res.StatusCode;
      [2]              Console.WriteLine("before save");
      [2]              DbContext.SaveChanges();

.. 在第一次迭代中,下一次迭代将在线程 #2 上继续,然后在等待之后,在另一个线程上继续。也许是#1,也许是#3。

要观察线程切换,可以添加:

Console.WriteLine(cardDetails.Status == null);
Console.WriteLine(string.Format("Thread #{0} (before)", Thread.CurrentThread.ManagedThreadId));
HttpResponseMessage res = await cons.PostAsJsonAsync("api/Cardmanagement/CardLoad", cardDetails);
Console.WriteLine(string.Format("Thread #{0} (after)", Thread.CurrentThread.ManagedThreadId));
cardDetails.Status = (int)res.StatusCode;

首先,移除异步调用/签名并尝试一个简单的同步解决方案来验证数据是否按预期持续存在。从那里您可以执行异步操作,但我会将数据加载、Web 服务调用和数据更新/保存操作包装到一个可等待的方法中,该方法使用单个 SaveChangesAsync 调用,其中 DbContext 范围为方法调用。

免责声明:如果这将作为控制台应用程序运行,则 async/await 完全没有必要。如果这是在 Web 服务器上处理请求的一部分,那么使用 Async/await 可以通过释放请求处理线程来使服务器对请求更敏感,同时后台线程通过 HTTP 通知和数据库更新进行搅动。考虑到选择记录进行处理的方式,该设计看起来并不适合并行处理(与异步/等待不同的乐趣桶)。并行运行最终可能会在一次运行中获取相同的 15 条记录中的一些。从我在代码中看到的情况来看,我会说异步没有任何好处,如果有的话,它会使其整体运行速度变慢。但是要概述异步方法,它看起来像这样:

using (var cons = new HttpClient())
{
    cons.BaseAddress = new Uri("http://cbsswfint01:53303/");
    cons.DefaultRequestHeaders.Accept.Clear();
    cons.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
    try
    {
       using(var context = new DbContext())
       {
           var cardsData = await context.MasterpassBulkLoad.Take(15).ToListAsync();
           foreach (var cardDetails in cardsData)
           {
                Console.WriteLine(cardDetails.Msisdn);
                Console.WriteLine(cardDetails.Status == null);
                HttpResponseMessage res = cons.PostAsJson("api/Cardmanagement/CardLoad", cardDetails);
                        cardDetails.Status = (int)res.StatusCode;
           }
           Console.WriteLine("before save");
           await context.SaveChangesAsync();
           Console.WriteLine("After Save");
       }
   }
   catch (Exception ex)
   {
           Console.WriteLine(ex.InnerException.Message);
           _logger.LogError(ex.InnerException.StackTrace);
   }
}

上面的代码会以 15 个批次保存结果,所以如果任何 1 行因任何原因失败,则不会保存。您还应该检查“Take 15”行为,因为它看起来总是会选择相同的 15 行。使用Take 时,您应该始终包含OrderBy 以保持一致的行为。我假设它指向一个正在检查状态的视图?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多