Lets say I design a generic read only repository IReadOnlyRepository
.
public interface IReadOnlyRepository
{
...
TEntity Find<TEntity>(object id) where TEntity : IEntity;
IEnumerable<TEntity> Filter<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
int Count<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
...
}
I already have a code base with several synchronous implementations of IReadOnlyRepository
and want to extend my type (interface and it's implementations) with asynchronous versions of the same methods since today it is cool to async all the way.
public interface IReadOnlyRepository
{
...
Task<TEntity> FindAsync<TEntity>(object id) where TEntity : IEntity;
Task<IEnumerable<TEntity>> FilterAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
Task<int> CountAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
}
This may sound subjective, but ...
Should I add async methods to the same file in a #region Async
?
public interface IReadOnlyRepository
{
...
#region Sync
TEntity Find<TEntity>(object id) where TEntity : IEntity;
IEnumerable<TEntity> Filter<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
int Count<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
#endregion
#region Async
Task<TEntity> FindAsync<TEntity>(object id) where TEntity : IEntity;
Task<IEnumerable<TEntity>> FilterAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
Task<int> CountAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
#endregion
...
}
... or should I make my repository partial and add async methods to partial files of the same type?
public partial interface IReadOnlyRepository
{
TEntity Find<TEntity>(object id) where TEntity : IEntity;
IEnumerable<TEntity> Filter<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
int Count<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
}
public partial interface IReadOnlyRepository
{
Task<TEntity> FindAsync<TEntity>(object id) where TEntity : IEntity;
Task<IEnumerable<TEntity>> FilterAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
Task<int> CountAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
}
... or should I add my async methods to a separate IRepositoryAsync
interface?
public interface IReadOnlyRepository
{
TEntity Find<TEntity>(object id) where TEntity : IEntity;
IEnumerable<TEntity> Filter<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
int Count<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
}
public interface IReadOnlyRepositoryAsync
{
Task<TEntity> FindAsync<TEntity>(object id) where TEntity : IEntity;
Task<IEnumerable<TEntity>> FilterAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
Task<int> CountAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
}
public class EfRepository : IReadOnlyRepository, IReadOnlyRepositoryAsync
... or should I get rid of sync methods at all and go async only?
public interface IReadOnlyRepository
{
Task<TEntity> FindAsync<TEntity>(object id) where TEntity : IEntity;
Task<IEnumerable<TEntity>> FilterAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
Task<int> CountAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : IEntity;
}
What are the best practices of composing large types with both sync and async methods in a matter of file structure, code readability and reusability?