修改实体后,DbContext.SaveChanges()不更新数据库

时间:2019-08-22 09:32:52

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

我正在使用实体框架从数据库中读取一些数据(读取成功),并且修改实体的效果很好,但是并不能将更改保存到数据库中。

该代码读取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();


        }

1 个答案:

答案 0 :(得分: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;

首先,删除异步调用/签名,然后尝试使用简单的同步解决方案来验证数据是否按预期持久。从那里可以执行异步操作,但是我可以通过单个SaveChangesAsync调用将数据加载,Web服务调用和数据更新/保存操作包装到单个可等待的方法中,而DbContext的范围仅限于该方法调用。 / p>

免责声明:如果要作为控制台应用程序运行,则完全不需要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以保持一致的行为。我假设它指向一个正在检查状态的视图?