From a341819711cda358652ad84b1e507d9559ecabfd Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 27 Apr 2021 18:22:57 +0800 Subject: refactor: Refactor data services. --- BackEnd/Timeline/Services/Data/DataManager.cs | 93 +++++++++------------------ 1 file changed, 29 insertions(+), 64 deletions(-) (limited to 'BackEnd/Timeline/Services/Data/DataManager.cs') diff --git a/BackEnd/Timeline/Services/Data/DataManager.cs b/BackEnd/Timeline/Services/Data/DataManager.cs index d9a4491d..9de17da3 100644 --- a/BackEnd/Timeline/Services/Data/DataManager.cs +++ b/BackEnd/Timeline/Services/Data/DataManager.cs @@ -1,73 +1,40 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using System; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Timeline.Entities; namespace Timeline.Services.Data { - /// - /// A data manager controlling data. - /// - /// - /// Identical data will be saved as one copy and return the same tag. - /// Every data has a ref count. When data is retained, ref count increase. - /// When data is freed, ref count decease. If ref count is decreased - /// to 0, the data entry will be destroyed and no longer occupy space. - /// - public interface IDataManager - { - /// - /// Saves the data to a new entry if it does not exist, - /// increases its ref count and returns a tag to the entry. - /// - /// The data. Can't be null. - /// The tag of the created entry. - /// Thrown when is null. - public Task RetainEntry(byte[] data); - - /// - /// Decrease the the ref count of the entry. - /// Remove it if ref count is zero. - /// - /// The tag of the entry. - /// Thrown when is null. - /// - /// It's no-op if entry with tag does not exist. - /// - public Task FreeEntry(string tag); - - /// - /// Retrieve the entry with given tag. If not exist, returns null. - /// - /// The tag of the entry. - /// The data of the entry. If not exist, returns null. - /// Thrown when is null. - public Task GetEntry(string tag); - } - public class DataManager : IDataManager { + private readonly ILogger _logger; private readonly DatabaseContext _database; private readonly IETagGenerator _eTagGenerator; - public DataManager(DatabaseContext database, IETagGenerator eTagGenerator) + public DataManager(ILogger logger, DatabaseContext database, IETagGenerator eTagGenerator) { + _logger = logger; _database = database; _eTagGenerator = eTagGenerator; } - public async Task RetainEntry(byte[] data) + public async Task RetainEntry(byte[] data, CancellationToken cancellationToken = default) { if (data == null) throw new ArgumentNullException(nameof(data)); - var tag = await _eTagGenerator.Generate(data); + var tag = await _eTagGenerator.GenerateETagAsync(data, cancellationToken); + + var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync(cancellationToken); + bool create; - var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync(); if (entity == null) { + create = true; entity = new DataEntity { Tag = tag, @@ -78,42 +45,54 @@ namespace Timeline.Services.Data } else { + create = false; entity.Ref += 1; } - await _database.SaveChangesAsync(); + await _database.SaveChangesAsync(cancellationToken); + + _logger.LogInformation(create ? Resource.LogDataManagerRetainEntryCreate : Resource.LogDataManagerRetainEntryAddRefCount, tag); return tag; } - public async Task FreeEntry(string tag) + public async Task FreeEntry(string tag, CancellationToken cancellationToken = default) { if (tag == null) throw new ArgumentNullException(nameof(tag)); - var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync(); + var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync(cancellationToken); if (entity != null) { + bool remove; + if (entity.Ref == 1) { + remove = true; _database.Data.Remove(entity); } else { + remove = false; entity.Ref -= 1; } - await _database.SaveChangesAsync(); + await _database.SaveChangesAsync(cancellationToken); + _logger.LogInformation(remove ? Resource.LogDataManagerFreeEntryRemove : Resource.LogDataManagerFreeEntryDecreaseRefCount, tag); + } + else + { + _logger.LogInformation(Resource.LogDataManagerFreeEntryNotExist, tag); } } - public async Task GetEntry(string tag) + public async Task GetEntry(string tag, CancellationToken cancellationToken = default) { if (tag == null) throw new ArgumentNullException(nameof(tag)); - var entity = await _database.Data.Where(d => d.Tag == tag).Select(d => new { d.Data }).SingleOrDefaultAsync(); + var entity = await _database.Data.Where(d => d.Tag == tag).Select(d => new { d.Data }).SingleOrDefaultAsync(cancellationToken); if (entity is null) return null; @@ -121,18 +100,4 @@ namespace Timeline.Services.Data return entity.Data; } } - - public static class DataManagerExtensions - { - /// - /// Try to get an entry and throw if not exist. - /// - public static async Task GetEntryAndCheck(this IDataManager dataManager, string tag, string notExistMessage) - { - var data = await dataManager.GetEntry(tag); - if (data is null) - throw new DatabaseCorruptedException($"Can't get data of tag {tag}. {notExistMessage}"); - return data; - } - } } -- cgit v1.2.3