From 57b29adef10dcefcacf255df05cf6bc11512caf2 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 5 Mar 2020 23:02:22 +0800 Subject: Implement data manager. --- Timeline/Services/DataManager.cs | 122 ++++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 39 deletions(-) (limited to 'Timeline/Services/DataManager.cs') diff --git a/Timeline/Services/DataManager.cs b/Timeline/Services/DataManager.cs index 005ad23c..66aa6f81 100644 --- a/Timeline/Services/DataManager.cs +++ b/Timeline/Services/DataManager.cs @@ -1,37 +1,19 @@ -using System; -using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using System; using System.Linq; using System.Threading.Tasks; +using Timeline.Entities; namespace Timeline.Services { - public class DataEntry - { -#pragma warning disable CA1819 // Properties should not return arrays - public byte[] Data { get; set; } = default!; -#pragma warning restore CA1819 // Properties should not return arrays - public DataInfo Info { get; set; } = default!; - } - - public class DataInfo - { - public DateTime Time { get; set; } - public string Type { get; set; } = default!; - } - /// - /// A data manager controling data. + /// A data manager controlling data. /// /// - /// All data to be saved will be checked identity. /// Identical data will be saved as one copy and return the same tag. - /// Every data has a ref count. When data is saved, ref count increase. - /// When data is removed, ref count decease. If ref count is decreased + /// 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. - /// - /// Type is just an attached attribute for convenience and not participate - /// in identity verification. This should be only used to save blobs but not - /// strings. It will be rare for identity blob with different type, I think. /// public interface IDataManager { @@ -40,11 +22,9 @@ namespace Timeline.Services /// increases its ref count and returns a tag to the entry. /// /// The data. Can't be null. - /// The type of the data. Can't be null. /// The tag of the created entry. - /// Thrown when or is null. - /// Thrown when a saved copy of data already exists but type is different. - public Task RetainEntry(byte[] data, string type); + /// Thrown when is null. + public Task RetainEntry(byte[] data); /// /// Decrease the the ref count of the entry. @@ -61,19 +41,83 @@ namespace Timeline.Services /// Retrieve the entry with given tag. /// /// The tag of the entry. - /// The entry. + /// The data of the entry. /// Thrown when is null. /// Thrown when entry with given tag does not exist. - public Task GetEntry(string tag); - - /// - /// Retrieve info of the entry with given tag. - /// - /// The tag of the entry. - /// The entry info. - /// Thrown when is null. - /// Thrown when entry with given tag does not exist. - public Task GetEntryInfo(string tag); + public Task GetEntry(string tag); } + public class DataManager : IDataManager + { + private readonly DatabaseContext _database; + private readonly IETagGenerator _eTagGenerator; + + public DataManager(DatabaseContext database, IETagGenerator eTagGenerator) + { + _database = database; + _eTagGenerator = eTagGenerator; + } + + public async Task RetainEntry(byte[] data) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); + + var tag = await _eTagGenerator.Generate(data); + + var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync(); + + if (entity == null) + { + entity = new DataEntity + { + Tag = tag, + Data = data, + Ref = 1 + }; + _database.Data.Add(entity); + await _database.SaveChangesAsync(); + } + else + { + entity.Ref += 1; + await _database.SaveChangesAsync(); + } + return tag; + } + + public async Task FreeEntry(string tag) + { + if (tag == null) + throw new ArgumentNullException(nameof(tag)); + + var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync(); + + if (entity != null) + { + if (entity.Ref == 1) + { + _database.Data.Remove(entity); + } + else + { + entity.Ref -= 1; + } + await _database.SaveChangesAsync(); + } + } + + public async Task GetEntry(string tag) + { + if (tag == null) + throw new ArgumentNullException(nameof(tag)); + + var entity = await _database.Data.Where(d => d.Tag == tag).Select(d => new { d.Data }).SingleOrDefaultAsync(); + + if (entity == null) + throw new InvalidOperationException("Entry with given tag does not exist."); + + return entity.Data; + } + } } -- cgit v1.2.3