private List <FileIndexItem> QueryGetNextPrevInFolder( string parentFolderPath) { List <FileIndexItem> LocalQuery(ApplicationDbContext context) { // sort by alphabet return(context.FileIndex.Where( p => p.ParentDirectory == parentFolderPath) .OrderBy(p => p.FileName).AsEnumerable() .GroupBy(i => i.FilePath).Select(g => g.First()).ToList()); } try { return(LocalQuery(_context)); } catch (MySqlProtocolException) { var context = new InjectServiceScope(_scopeFactory).Context(); return(LocalQuery(context)); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); return(LocalQuery(context)); } }
internal List <FileIndexItem> QueryDisplayFileFolders(string subPath = "/") { List <FileIndexItem> QueryItems(ApplicationDbContext context) { var queryItems = context.FileIndex .TagWith("QueryDisplayFileFolders") .Where(p => p.ParentDirectory == subPath && p.FileName != "/") .OrderBy(p => p.FileName).AsEnumerable() // remove duplicates from list .GroupBy(t => t.FileName).Select(g => g.First()); return(queryItems.OrderBy(p => p.FileName, StringComparer.InvariantCulture).ToList()); } try { return(QueryItems(_context)); } catch (NotSupportedException) { // System.NotSupportedException: The ReadAsync method cannot be called when another read operation is pending. var context = new InjectServiceScope(_scopeFactory).Context(); return(QueryItems(context)); } catch (InvalidOperationException) // or ObjectDisposedException { var context = new InjectServiceScope(_scopeFactory).Context(); return(QueryItems(context)); } }
/// <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> /// Add a new item to the database /// </summary> /// <param name="updateStatusContent">the item</param> /// <returns>item with id</returns> public FileIndexItem AddItem(FileIndexItem updateStatusContent) { if (string.IsNullOrWhiteSpace(updateStatusContent.FileName) && updateStatusContent.IsDirectory == false) { throw new MissingFieldException("use filename (exception: the root folder can have no name)"); } try { _context.FileIndex.Add(updateStatusContent); _context.SaveChanges(); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); context.FileIndex.Add(updateStatusContent); context.SaveChanges(); } catch (DbUpdateConcurrencyException e) { _logger?.LogInformation("AddItem catch-ed DbUpdateConcurrencyException (ignored)", e); } AddCacheItem(updateStatusContent); return(updateStatusContent); }
/// <summary> /// Query all FileIndexItems with the type folder /// </summary> /// <returns>List of all folders in database, including content</returns> public List <FileIndexItem> GetAllFolders() { try { return(_context.FileIndex.Where(p => p.IsDirectory == true).ToList()); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); return(context.FileIndex.Where(p => p.IsDirectory == true).ToList()); } }
/// <summary> /// Remove a new item from the database (NOT from the file system) /// </summary> /// <param name="updateStatusContent">the FileIndexItem with database data</param> /// <returns></returns> public FileIndexItem RemoveItem(FileIndexItem updateStatusContent) { void LocalQuery(ApplicationDbContext context) { // Detach first https://stackoverflow.com/a/42475617 var local = context.Set <FileIndexItem>() .Local .FirstOrDefault(entry => entry.Id.Equals(updateStatusContent.Id)); if (local != null) { context.Entry(local).State = EntityState.Detached; } context.Attach(updateStatusContent).State = EntityState.Deleted; context.FileIndex.Remove(updateStatusContent); context.SaveChanges(); context.Attach(updateStatusContent).State = EntityState.Detached; } try { LocalQuery(_context); } catch (ObjectDisposedException disposedException) { _logger?.LogInformation("catch-ed disposedException:", disposedException); var context = new InjectServiceScope(_scopeFactory).Context(); LocalQuery(context); } catch (DbUpdateConcurrencyException concurrencyException) { _logger?.LogInformation("catch-ed concurrencyException:", concurrencyException); try { _context.SaveChanges(); } catch (DbUpdateConcurrencyException e) { _logger?.LogInformation(e, "[RemoveItem] save failed after DbUpdateConcurrencyException"); } } // remove parent directory cache RemoveCacheItem(updateStatusContent); // remove getFileHash Cache ResetItemByHash(updateStatusContent.FileHash); return(updateStatusContent); }
/// <summary> /// Add a new item to the database /// </summary> /// <param name="fileIndexItem">the item</param> /// <returns>item with id</returns> public virtual async Task <FileIndexItem> AddItemAsync(FileIndexItem fileIndexItem) { async Task <FileIndexItem> LocalDefaultQuery() { var context = new InjectServiceScope(_scopeFactory).Context(); return(await LocalQuery(context)); } async Task <FileIndexItem> LocalQuery(ApplicationDbContext context) { // only in test case fileIndex is null if (context.FileIndex != null) { await context.FileIndex.AddAsync(fileIndexItem); } await context.SaveChangesAsync(); // Fix for: The instance of entity type 'Item' cannot be tracked because // another instance with the same key value for {'Id'} is already being tracked context.Entry(fileIndexItem).State = EntityState.Unchanged; AddCacheItem(fileIndexItem); return(fileIndexItem); } try { return(await LocalQuery(_context)); } catch (Microsoft.Data.Sqlite.SqliteException e) { _logger?.LogInformation(e, $"catch-ed SqliteException going to retry 2 times {fileIndexItem.FilePath}"); return(await RetryHelper.DoAsync( LocalDefaultQuery, TimeSpan.FromSeconds(2), 2)); } catch (DbUpdateException e) { _logger?.LogInformation(e, $"catch-ed DbUpdateException going to retry 2 times {fileIndexItem.FilePath}"); return(await RetryHelper.DoAsync( LocalDefaultQuery, TimeSpan.FromSeconds(2), 2)); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); return(await LocalQuery(context)); } }
/// <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 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); }
private async Task <string> QueryGetItemByHashAsync(string fileHash) { async Task <string> LocalQueryGetItemByHashAsync(ApplicationDbContext context) { return((await context.FileIndex.TagWith("QueryGetItemByHashAsync").FirstOrDefaultAsync( p => p.FileHash == fileHash && p.IsDirectory != true ))?.FilePath); } try { return(await LocalQueryGetItemByHashAsync(_context)); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); return(await LocalQueryGetItemByHashAsync(context)); } }
// Return a File Item By it Hash value // New added, directory hash now also hashes private string QueryGetItemByHash(string fileHash) { string LocalQueryGetItemByHash(ApplicationDbContext context) { return(context.FileIndex.TagWith("QueryGetItemByHash").FirstOrDefault( p => p.FileHash == fileHash && p.IsDirectory != true )?.FilePath); } try { return(LocalQueryGetItemByHash(_context)); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); return(LocalQueryGetItemByHash(context)); } }
/// <summary> /// (Sync) Add a new item to the database /// </summary> /// <param name="fileIndexItemList"></param> /// <returns>items with id</returns> public List <FileIndexItem> AddRange(List <FileIndexItem> fileIndexItemList) { try { _context.FileIndex.AddRange(fileIndexItemList); _context.SaveChanges(); } catch (ObjectDisposedException) { var context = new InjectServiceScope(_scopeFactory).Context(); context.FileIndex.AddRange(fileIndexItemList); context.SaveChanges(); } foreach (var fileIndexItem in fileIndexItemList) { AddCacheItem(fileIndexItem); } return(fileIndexItemList); }
/// <summary> /// Get all objects inside a folder /// </summary> /// <param name="filePaths">parent paths</param> /// <param name="fallbackDelay">delay when fallback</param> /// <returns>list of all objects inside the folder</returns> public async Task <List <FileIndexItem> > GetAllObjectsAsync(List <string> filePaths, int fallbackDelay = 5000) { if (!filePaths.Any()) { return(new List <FileIndexItem>()); } async Task <List <FileIndexItem> > LocalGetAllObjectsAsync() { var dbContext = new InjectServiceScope(_scopeFactory).Context(); var result = FormatOk(await GetAllObjectsQuery(dbContext, filePaths) .ToListAsync()); await dbContext.DisposeAsync(); return(result); } try { return(FormatOk(await GetAllObjectsQuery(_context, filePaths) .ToListAsync())); } catch (ObjectDisposedException) { return(await LocalGetAllObjectsAsync()); } catch (InvalidOperationException) { // System.InvalidOperationException: ExecuteReader can only be called when the connection is open. return(await LocalGetAllObjectsAsync()); } catch (MySqlConnector.MySqlException exception) { _logger.LogError(exception, $"catch-ed mysql error [next delay {fallbackDelay}]"); await Task.Delay(fallbackDelay); return(await LocalGetAllObjectsAsync()); } }