I am implementing a repository layer in my mobile application. I would like the repository layer to complete to abstract the details about where the data it coming from/ or how we retrieve it from the service layer.
I have three data sources in my application
- Data from a remote API
- Data from a local SQLite DB
- Data from a local In-memory cache
And I would like to access data using the following priority
- Remote Only
- Local Only
- Remote First
- Local First
I am using a generic repository pattern with Unit of work and it works great to handle all the DB transactions. But when I bring in the other 2 data-sources into the picture I will a bit confused on how to structure the code.
public interface IGenericRepository<TEntity> where TEntity : class, new()
{
Task<TEntity> Get(string id);
Task<IEnumerable<TEntity>> GetAll();
Task<IEnumerable<TEntity>> Find<TValue>(Expression<Func<TEntity, bool>> predicate);
Task<int> Add(TEntity entity);
Task<int> AddRange(IEnumerable<TEntity> entities);
Task<int> Remove(TEntity entity);
Task<int> RemoveRange(IEnumerable<TEntity> entities);
Task<int> UpdateItem(TEntity entity);
Task<int> UpdateAllItems(IEnumerable<TEntity> entities);
}
Here is the implementation of the interface
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class, new()
{
private Lazy<IEncryptedDBConnection> _lazyDBConnection;
protected IEncryptedDBConnection _dbConnection => _lazyDBConnection.Value;
public GenericRepository(Lazy<IEncryptedDBConnection> lazyDBConnection)
{
_lazyDBConnection = lazyDBConnection;
}
public virtual async Task<TEntity> Get(string id)
{
return await AttemptAndRetry(() => _dbConnection.GetAsyncConnection().FindAsync<TEntity>(id)).ConfigureAwait(false);
}
public virtual async Task<IEnumerable<TEntity>> GetAll()
{
return await AttemptAndRetry(() => _dbConnection.GetAsyncConnection().Table<TEntity>().ToListAsync()).ConfigureAwait(false);
}
public virtual async Task<IEnumerable<TEntity>> Find<TValue>(Expression<Func<TEntity, bool>> predicate)
{
return await AttemptAndRetry(async () =>
{
var query = _dbConnection.GetAsyncConnection().Table<TEntity>();
if (predicate != null)
query = query.Where(predicate);
return await query.ToListAsync();
}).ConfigureAwait(false);
}
public virtual async Task<int> Add(TEntity entity)
{
return await AttemptAndRetry(() => _dbConnection.GetAsyncConnection().InsertAsync(entity)).ConfigureAwait(false);
}
public virtual async Task<int> AddRange(IEnumerable<TEntity> entities)
{
return await AttemptAndRetry(() => _dbConnection.GetAsyncConnection().InsertAllAsync(entities)).ConfigureAwait(false);
}
public virtual async Task<int> Remove(TEntity entity)
{
return await AttemptAndRetry(() => _dbConnection.GetAsyncConnection().DeleteAsync(entity)).ConfigureAwait(false);
}
public virtual async Task<int> RemoveRange(IEnumerable<TEntity> entities)
{
return await AttemptAndRetry(() => _dbConnection.GetAsyncConnection().DeleteAllAsync<TEntity>()).ConfigureAwait(false);
}
public virtual async Task<int> UpdateItem(TEntity entity)
{
return await AttemptAndRetry(() => _dbConnection.GetAsyncConnection().UpdateAsync(entity)).ConfigureAwait(false);
}
public virtual async Task<int> UpdateAllItems(IEnumerable<TEntity> entities)
{
return await AttemptAndRetry(() => _dbConnection.GetAsyncConnection().UpdateAllAsync(entities)).ConfigureAwait(false);
}
protected Task<TResult> AttemptAndRetry<TResult>(Func<Task<TResult>> action, int numRetries = 3)
{
return Policy.Handle<SQLiteException>().WaitAndRetryAsync(numRetries, PollyRetryAttempt).ExecuteAsync(action);
}
private TimeSpan PollyRetryAttempt(int attemptNumber) => TimeSpan.FromMilliseconds(Math.Pow(2, attemptNumber));
}
I also have various other classes that inherit GenericRepsotiory to make any additonal chnages such as ProductRepository, OrderRepository etc.
My Questions
- Which is the right class to add the logic to choose the data access source whether remote only or local only etc?
- Do I add the remote API calls into the repository classes or do I write other classes to make the actual calls?
Aucun commentaire:
Enregistrer un commentaire