Skip to content

Instantly share code, notes, and snippets.

@aevitas
Created June 4, 2019 12:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aevitas/a7a88283ca25982adfd758bf4e969085 to your computer and use it in GitHub Desktop.
Save aevitas/a7a88283ca25982adfd758bf4e969085 to your computer and use it in GitHub Desktop.
// 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