Example #1
0
        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));
            }
        }
Example #2
0
        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));
            }
        }
Example #3
0
        /// <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);
                }
            }
        }
Example #4
0
        /// <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);
        }
Example #5
0
 /// <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());
     }
 }
Example #6
0
        /// <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);
        }
Example #7
0
        /// <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));
            }
        }
Example #8
0
        /// <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");
                }
            }
        }
Example #9
0
        /// <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);
        }
Example #10
0
        /// <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);
        }
Example #11
0
        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));
            }
        }
Example #12
0
        // 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));
            }
        }
Example #13
0
        /// <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);
        }
Example #14
0
        /// <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());
            }
        }