/// <summary> /// Update item in Database Async /// You should update the cache yourself (so this is NOT done) /// </summary> /// <param name="updateStatusContentList">content to update</param> /// <returns>same item</returns> public async Task <List <FileIndexItem> > UpdateItemAsync(List <FileIndexItem> updateStatusContentList) { if (!updateStatusContentList.Any()) { return(new List <FileIndexItem>()); } async Task <List <FileIndexItem> > LocalQuery(DbContext context, List <FileIndexItem> fileIndexItems) { foreach (var item in fileIndexItems) { item.SetLastEdited(); context.Attach(item).State = EntityState.Modified; } await context.SaveChangesAsync(); foreach (var item in fileIndexItems) { context.Attach(item).State = EntityState.Detached; } CacheUpdateItem(fileIndexItems); return(fileIndexItems); } try { return(await LocalQuery(_context, updateStatusContentList)); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); try { return(await LocalQuery(context, updateStatusContentList)); } catch (DbUpdateConcurrencyException concurrencyException) { SolveConcurrency.SolveConcurrencyExceptionLoop(concurrencyException.Entries); return(await LocalQuery(context, updateStatusContentList)); } } catch (DbUpdateConcurrencyException concurrencyException) { SolveConcurrency.SolveConcurrencyExceptionLoop(concurrencyException.Entries); try { return(await LocalQuery(_context, updateStatusContentList)); } catch (DbUpdateConcurrencyException e) { var items = await GetObjectsByFilePathQueryAsync(updateStatusContentList .Select(p => p.FilePath).ToList()); _logger?.LogInformation($"double error UCL:{updateStatusContentList.Count} Count: {items.Count}", e); return(updateStatusContentList); } } }
/// <summary> /// Retry when an Exception has occured /// </summary> /// <param name="updateStatusContent"></param> /// <param name="e">Exception</param> private async Task RetrySaveChangesAsync(FileIndexItem updateStatusContent, Exception e) { _logger?.LogInformation(e, "[RetrySaveChangesAsync] retry catch-ed exception "); _logger?.LogInformation("[RetrySaveChangesAsync] next retry ~>"); async Task LocalRetrySaveChangesAsyncQuery() { // InvalidOperationException: A second operation started on this context before a previous operation completed. // https://go.microsoft.com/fwlink/?linkid=2097913 await Task.Delay(5); var context = new InjectServiceScope(_scopeFactory).Context(); context.Attach(updateStatusContent).State = EntityState.Modified; await context.SaveChangesAsync(); context.Attach(updateStatusContent).State = EntityState.Detached; await context.DisposeAsync(); } try { await LocalRetrySaveChangesAsyncQuery(); } catch (MySqlException mySqlException) { _logger?.LogError(mySqlException, "[RetrySaveChangesAsync] MySqlException catch-ed and retry again"); await LocalRetrySaveChangesAsyncQuery(); } catch (DbUpdateConcurrencyException concurrencyException) { SolveConcurrency.SolveConcurrencyExceptionLoop(concurrencyException.Entries); try { _logger?.LogInformation("[RetrySaveChangesAsync] SolveConcurrencyExceptionLoop disposed item"); var context = new InjectServiceScope(_scopeFactory).Context(); await context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException retry2Exception) { _logger?.LogInformation(retry2Exception, "[RetrySaveChangesAsync] save failed after DbUpdateConcurrencyException"); } } }
/// <summary> /// Update one single item in the database /// For the API/update endpoint /// </summary> /// <param name="updateStatusContent">content to updated</param> /// <returns>this item</returns> public FileIndexItem UpdateItem(FileIndexItem updateStatusContent) { void LocalUpdateItemQuery(ApplicationDbContext context) { // Update te last edited time manual updateStatusContent.SetLastEdited(); context.Attach(updateStatusContent).State = EntityState.Modified; context.SaveChanges(); context.Attach(updateStatusContent).State = EntityState.Detached; CacheUpdateItem(new List <FileIndexItem> { updateStatusContent }); } try { LocalUpdateItemQuery(_context); } catch (ObjectDisposedException error) { _logger?.LogInformation(error, "[UpdateItem] catch-ed ObjectDisposedException"); var context = new InjectServiceScope(_scopeFactory).Context(); LocalUpdateItemQuery(context); } catch (InvalidOperationException) { var context = new InjectServiceScope(_scopeFactory).Context(); LocalUpdateItemQuery(context); } catch (DbUpdateConcurrencyException concurrencyException) { SolveConcurrency.SolveConcurrencyExceptionLoop(concurrencyException.Entries); try { _context.SaveChanges(); } catch (DbUpdateConcurrencyException e) { _logger?.LogError(e, "[UpdateItem] save failed after DbUpdateConcurrencyException"); } } return(updateStatusContent); }
/// <summary> /// Update one single item in the database /// For the API/update endpoint /// </summary> /// <param name="updateStatusContent">content to updated</param> /// <returns>this item</returns> public async Task <FileIndexItem> UpdateItemAsync(FileIndexItem updateStatusContent) { async Task LocalQuery(DbContext context, FileIndexItem fileIndexItem) { // Update te last edited time manual fileIndexItem.SetLastEdited(); context.Attach(fileIndexItem).State = EntityState.Modified; await context.SaveChangesAsync(); context.Attach(fileIndexItem).State = EntityState.Detached; CacheUpdateItem(new List <FileIndexItem> { updateStatusContent }); SetGetObjectByFilePathCache(fileIndexItem.FilePath, updateStatusContent, TimeSpan.FromMinutes(1)); } try { await LocalQuery(_context, updateStatusContent); } catch (ObjectDisposedException e) { await RetrySaveChangesAsync(updateStatusContent, e); } catch (InvalidOperationException e) { // System.InvalidOperationException: Can't replace active reader. await RetrySaveChangesAsync(updateStatusContent, e); } catch (DbUpdateConcurrencyException concurrencyException) { SolveConcurrency.SolveConcurrencyExceptionLoop(concurrencyException.Entries); try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException e) { _logger?.LogInformation(e, "[UpdateItemAsync] save failed after DbUpdateConcurrencyException"); } } return(updateStatusContent); }
/// <summary> /// Update a list of items in the index /// Used for the API/update endpoint /// </summary> /// <param name="updateStatusContentList">list of items to be updated</param> /// <returns>the same list, and updated in the database</returns> public List <FileIndexItem> UpdateItem(List <FileIndexItem> updateStatusContentList) { void LocalQuery(ApplicationDbContext context) { foreach (var item in updateStatusContentList) { item.SetLastEdited(); context.Attach(item).State = EntityState.Modified; } context.SaveChanges(); } try { LocalQuery(_context); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); LocalQuery(context); } catch (InvalidOperationException) { var context = new InjectServiceScope(_scopeFactory).Context(); LocalQuery(context); } catch (DbUpdateConcurrencyException concurrencyException) { SolveConcurrency.SolveConcurrencyExceptionLoop(concurrencyException.Entries); try { _context.SaveChanges(); } catch (DbUpdateConcurrencyException e) { _logger?.LogError(e, "[List<>UpdateItem] save failed after DbUpdateConcurrencyException"); } } CacheUpdateItem(updateStatusContentList); return(updateStatusContentList); }
/// <summary> /// Add a new item to the database /// </summary> /// <param name="fileIndexItemList"></param> /// <returns>items with id</returns> public virtual async Task <List <FileIndexItem> > AddRangeAsync(List <FileIndexItem> fileIndexItemList) { async Task LocalQuery(ApplicationDbContext context) { await context.FileIndex.AddRangeAsync(fileIndexItemList); await context.SaveChangesAsync(); } try { await LocalQuery(_context); } catch (DbUpdateConcurrencyException concurrencyException) { SolveConcurrency.SolveConcurrencyExceptionLoop(concurrencyException.Entries); try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException e) { _logger?.LogError(e, "[AddRangeAsync] save failed after DbUpdateConcurrencyException"); } } catch (ObjectDisposedException) { await LocalQuery(new InjectServiceScope(_scopeFactory).Context()); } foreach (var fileIndexItem in fileIndexItemList) { AddCacheItem(fileIndexItem); } return(fileIndexItemList); }