locked
Why does my web application hang up on "await _context.SaveChangesAsync();" RRS feed

  • Question

  • User-1849651236 posted

    Hello,

    I have an Asp.Net Core 2.1, MVC, C# Web application in Visual Studio 2017 15.7.5.  My Nuget packages show that I am using Microsoft.EntityFrameworkCore.Tools 2.1.1.

    In my coding shown at the end of this post, my program hangs up on this statement:

    await _context.SaveChangesAsync();

    and never returns control to the next statement.  I used the Visual Studio debugger to stop on the statement after that one and waited ten minutes.  It never gave control back.

    I also tried it this way and got the same results:

    _context.AddRange(productsRootObject.Products);
    _context.SaveChanges();

    Does anyone know how to debug this issue?  Is there some log that I can look at?

    Thanks,
    Tony

    Here is my code:

        public class ProductListViewComponent : ViewComponent
        {
              private readonly ChinavasionAPIContext _context;
              private readonly IHubContext<ChvHub> _hubContext;
    
              public ProductListViewComponent(IHubContext<ChvHub> hubcontext, ChinavasionAPIContext context)
              {
                   _context = context;
                   _hubContext = hubcontext;
              }
    
              #region snippet1
              public async Task<IViewComponentResult> InvokeAsync()
              {
                   //string MyView = "Default";
                   var items = await GetItemsAsync();
                   await this._hubContext.Clients.All.SendAsync("ReceiveMessage", "", "===============================================  Ready to Display Lakeside and Chinavasion Products  ===============================================");
    
                   return View(items);
              }
    
              public async Task<List<ProductViewModel>> GetItemsAsync()
              {
                   var model = new AccessModel();
                   model.UserAccessModel = _context.UserAccessModels.Single(a => a.ID == 1);
    
                   var accessToken = (model.UserAccessModel.AccessToken ?? TempData["accessToken"]).ToString();
                   var serverUrl = (model.UserAccessModel.ServerUrl ?? TempData["serverUrl"]).ToString();
                   var nopApiClient = new ApiClient(accessToken, serverUrl);
                   MultipleProductModel multipleProductModel = new MultipleProductModel();
                   List<Category> categories = new List<Category>();
    
                   string jsonUrl = $"/api/products/count";
                   object productsCount = nopApiClient.Get(jsonUrl);
                   var nopProductsCount = JsonConvert.DeserializeObject<ProductsCount>(productsCount.ToString());
                   int sinceID = 0;
                   int productCount = 0;
                   await this._hubContext.Clients.All.SendAsync("ReceiveMessage", "", "=============================================  Starting Lakeside Product Retrieval from Live Database  ==============================================");
    
                   do
                   {
                        productCount += 1;
                        await this._hubContext.Clients.All.SendAsync("ReceiveMessage", productCount.ToString(), " Downloading Lakeside products");
                        jsonUrl = $"/api/products?limit=250&since_id=" + sinceID.ToString() + $"&fields=id,sku,name,images,categories";
                        object productsData = nopApiClient.Get(jsonUrl);
    
                        var productsRootObject = JsonConvert.DeserializeObject<ProductsRootObject>(productsData.ToString());
                        multipleProductModel.MProductsApi.AddRange(productsRootObject.Products);
                        await _context.AddRangeAsync(productsRootObject.Products);
                        await _context.SaveChangesAsync();
    
                        var last = multipleProductModel.MProductsApi.LastOrDefault();
                        sinceID = Convert.ToInt16(last.Id);
                   } while (multipleProductModel.MProductsApi.Count < nopProductsCount.Count);
    }
    using System.Collections.Generic;
    using Newtonsoft.Json;
    
    namespace ChinavasionAPI.DTOs
    {
        public class ProductsRootObject
        {
            [JsonProperty("products")]
            public List<ProductApi> Products { get; set; }
        }
    }
    using Newtonsoft.Json;
    using System.Collections.Generic;
    
    {
         public class ProductApi
         {
              [JsonProperty("id")]
              public string Id { get; set; }
              [JsonProperty("sku")]
              public string Sku { get; set; }
              [JsonProperty("name")]
              public string Name { get; set; }
              [JsonProperty("images")]
              public List<Image> Images { get; set; }
              [JsonProperty("categories")]
              public string Categories { get; set; }
         }
    
         public class Image
         {
              [JsonProperty("id")]
              public int Id { get; set; }
              [JsonProperty("position")]
              public int Position { get; set; }
              [JsonProperty("src")]
              public string Src { get; set; }
              [JsonProperty("attachment")]
              public string Attachment { get; set; }
         }
    }
    using System.Collections.Generic;
    using ChinavasionAPI.DTOs;
    
    namespace ChinavasionAPI.Models.CAPIViewModels
    {
         public class MultipleProductModel
         {
              public List<ProductApi> MProductsApi { get; set; }
              public List<Product> MProducts { get; set; }
              public MultipleProductModel()
              {
                   this.MProductsApi = new List<ProductApi>();
                   this.MProducts = new List<Product>();
              }
         }
    }
    

    Friday, August 3, 2018 12:30 PM

Answers

  • User-1849651236 posted

    Wenushka,

    Your suggested change did not work.

    I put in a try/catch on the code where it was hanging up.  I saw this error:

    An error occurred while updating the entries. See the inner exception for details.
    Cannot insert explicit value for identity column in table 'ProductApis' when IDENTITY_INSERT is set to OFF.

    After Googling that error, I found articles that suggested putting this annotation:

    [DatabaseGenerated(DatabaseGeneratedOption.None)]

    on the Id properties for the ProductApi and Image models.

    That allowed me to run the program without getting the hangup and it populated the tables.

    I very much appreciate your help in trying to fix this issue.

    Thanks,
    Tony

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, August 9, 2018 2:11 PM

All replies

  • User1881638666 posted

    Hi TGirgenti,

    If there is blocking call inside asynchronous call / path, it could cause deadlock if the both the threads waiting for each other to complete (SynchronizationContext).

    This is what happened in your asp.net mvc application.

    There is a blocking (sync) call in your method, 

    object productsData = nopApiClient.Get(jsonUrl);

    Fix this, by calling the asynchronous version of the method if available. eg: await nopApiClient.GetAsync(jsonUrl) ;

    Note : Fix the both two places where you called blocking Get method inside the method.

    Further go through the followings,

    https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

    https://gist.github.com/jonlabelle/841146854b23b305b50fa5542f84b20c

    Thanks,

    Wenushka

    Sunday, August 5, 2018 5:42 AM
  • User-1849651236 posted

    Hello Wenushka.

    Thanks for your help with this issue.

    There is one thing that I am confused about.  If the blocking call is causing the problem when it gets to this:

    await _context.SaveChangesAsync();

    why does it not hangup on the await prior to that one?

    await _context.AddRangeAsync(productsRootObject.Products);

    I will read the information you pointed out and I will try to make the blocking Get you pointed out to an await

    Thanks,
    Tony

    Sunday, August 5, 2018 4:55 PM
  • User-1849651236 posted

    Hello Wenushka.

    I tried your idea of making the calling of the asynchronous version of the methods and it did not work.

    I changed all of my methods to public async Task and it still hangs up on "await _context.SaveChangesAsync();"

    Does anyone have any idea of how to fix this problem?

    Thanks,
    Tony

    Tuesday, August 7, 2018 2:33 AM
  • User1881638666 posted

    Hi TGirgenti,

    Could you share the implementation of the ,

    ChinavasionAPIContext context

    or two methods of that class,

    await _context.AddRangeAsync(productsRootObject.Products);
    await _context.SaveChangesAsync();

    Thanks,

    Wenushka

    Tuesday, August 7, 2018 5:05 AM
  • User-1849651236 posted

    Wenushka,

    Here is my context file:

    using ChinavasionAPI.Models;
    using Microsoft.EntityFrameworkCore;
    
    namespace ChinavasionAPI.Data
    {
        public class ChinavasionAPIContext : DbContext
        {
            public ChinavasionAPIContext (DbContextOptions<ChinavasionAPIContext> options) : base(options)
            {
            }
    
              public DbSet<ChinavasionAPI.Models.QueryCall> QueryCalls { get; set; }
              public DbSet<ChinavasionAPI.Models.Setup> Setups { get; set; }
              public DbSet<ChinavasionAPI.Models.QueryAssignment> QueryAssignments { get; set; }
              public DbSet<ChinavasionAPI.Models.UserAccessModel> UserAccessModels { get; set; }
              public DbSet<ChinavasionAPI.DTOs.ProductApi> ProductApis { get; set; }
              public DbSet<ChinavasionAPI.DTOs.Image> Images { get; set; }
    
              protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                 modelBuilder.Entity<QueryCall>().ToTable("QueryCall");
                 modelBuilder.Entity<Setup>().ToTable("Setup");
                 modelBuilder.Entity<UserAccessModel>().ToTable("UserAccessModel");
                 modelBuilder.Entity<QueryAssignment>().ToTable("QueryAssignment");
                 modelBuilder.Entity<QueryAssignment>()
                        .HasKey(q => new { q.QueryID, q.SetupID });
            }
         }
    }
    

    Thanks for your help.

    Tony

    Tuesday, August 7, 2018 12:59 PM
  • User1881638666 posted

    hi TGirgenti,

    Change how the entities is added before committing the changes to db.

    Change, 

    await _context.AddRangeAsync(productsRootObject.Products);

    with, 

    _context.ProductApis.AddRange(productsRootObject.Products);

    Note: In adding the entities, you don't have to call async version of DbContext unless we have special requirement mentioned here.

    https://docs.microsoft.com/en-us/ef/core/api/microsoft.entityframeworkcore.dbcontext

    https://docs.microsoft.com/en-us/ef/core/api/microsoft.entityframeworkcore.dbset-1

    Finally, commit the changes as usual with the,

    await _context.SaveChangesAsync();


     

    Wednesday, August 8, 2018 9:02 AM
  • User-1849651236 posted

    Wenushka,

    Your suggested change did not work.

    I put in a try/catch on the code where it was hanging up.  I saw this error:

    An error occurred while updating the entries. See the inner exception for details.
    Cannot insert explicit value for identity column in table 'ProductApis' when IDENTITY_INSERT is set to OFF.

    After Googling that error, I found articles that suggested putting this annotation:

    [DatabaseGenerated(DatabaseGeneratedOption.None)]

    on the Id properties for the ProductApi and Image models.

    That allowed me to run the program without getting the hangup and it populated the tables.

    I very much appreciate your help in trying to fix this issue.

    Thanks,
    Tony

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, August 9, 2018 2:11 PM