Created
June 4, 2019 12:06
-
-
Save aevitas/a7a88283ca25982adfd758bf4e969085 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This implementation is of rather poor quality - you should only use this class for debugging and testing purposes, and never for actual production code. | |
public class FileRepository<T> : IRepository<T>, IDisposable, IAsyncDisposable where T : class | |
{ | |
private readonly ConcurrentQueue<T> _writeQueue = new ConcurrentQueue<T>(); | |
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); | |
private readonly FileInfo _file; | |
private readonly CancellationTokenSource _cts = new CancellationTokenSource(); | |
private readonly Task _writeTask; | |
public FileRepository(IOptions<FileRepositoryOptions> options) | |
{ | |
var opts = options?.Value ?? throw new ArgumentNullException(nameof(options)); | |
if (string.IsNullOrWhiteSpace(opts.FileName)) | |
throw new ArgumentException("File Repositories must specify a file to use as their backing store."); | |
var fi = new FileInfo(opts.FileName); | |
_file = fi; | |
_writeTask = Task.Run(async () => | |
{ | |
while (true) | |
{ | |
if (_writeQueue.IsEmpty) | |
{ | |
await Task.Delay(TimeSpan.FromSeconds(3)); | |
continue; | |
} | |
_lock.EnterWriteLock(); | |
try | |
{ | |
while (_writeQueue.TryDequeue(out var entry)) | |
{ | |
await using var sw = _file.AppendText(); | |
var serialized = JsonConvert.SerializeObject(entry); | |
await sw.WriteLineAsync(serialized); | |
} | |
} | |
finally | |
{ | |
_lock.ExitWriteLock(); | |
} | |
} | |
}, _cts.Token); | |
} | |
public Task<T> AddAsync(T value) | |
{ | |
Requires.NotNull(value, nameof(value)); | |
_writeQueue.Enqueue(value); | |
return Task.FromResult(value); | |
} | |
// This method is very slow compared to actual database selects, for obvious reasons. | |
public async Task<T> GetAsync(Expression<Func<T, bool>> predicate) | |
{ | |
_lock.EnterReadLock(); | |
try | |
{ | |
var objects = await GetObjectsFromFileAsync(); | |
var func = predicate.Compile(); | |
return objects.FirstOrDefault(func); | |
} | |
finally | |
{ | |
_lock.ExitReadLock(); | |
} | |
} | |
public Task<T> UpdateAsync(T value) | |
{ | |
throw new NotSupportedException(); | |
} | |
public Task<bool> DeleteAsync(Expression<Func<T, bool>> predicate) | |
{ | |
throw new NotSupportedException(); | |
} | |
public async Task<IEnumerable<T>> ListAsync(Expression<Func<T, bool>> predicate) | |
{ | |
_lock.EnterReadLock(); | |
try | |
{ | |
var objects = await GetObjectsFromFileAsync(); | |
var func = predicate.Compile(); | |
return objects.Where(func); | |
} | |
finally | |
{ | |
_lock.ExitReadLock(); | |
} | |
} | |
private async Task<IEnumerable<T>> GetObjectsFromFileAsync() | |
{ | |
using var stream = _file.OpenText(); | |
var content = await stream.ReadToEndAsync(); | |
var objects = JsonConvert.DeserializeObject<List<T>>(content); | |
return objects; | |
} | |
public void Dispose() | |
{ | |
_lock?.Dispose(); | |
_cts?.Dispose(); | |
_writeTask?.Dispose(); | |
} | |
public async ValueTask DisposeAsync() | |
{ | |
_cts.Cancel(); | |
await _writeTask; | |
_lock?.Dispose(); | |
_cts?.Dispose(); | |
_writeTask?.Dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment