Learn how to implement a better enhanced Pattern following Best Practices to satisfy extended requirements like throttling. Repository The Story My team and I were going to start working on a big project. It was about a Web Application for a huge company. You can’t imagine tempted I am to reveal its name, but unfortunately, I can’t. The client had some specific requirements and some of them were actually technical ones. I know this is not a normal thing, but, this client already had their own team of technical people so that they could cope with our team. Long story short, let me skip the too-long list of requirements and jump to the ones I am actually interested in for this article. Requirements As I said, our client had some specific requirements and some of them were technical ones. So, let me walk you through some of these interesting requirements and I am sure you would fall in love with them. The application needed to have a backend to store all the data processed. And, Once I say , you say… . Backend Repository The most interesting part of the requirements was related to the Repository to be implemented. Therefore, let me tell you about these ones. Scheduled Automatic Health Check on Repositories : The client wanted to be able to design, implement and run automatic health checks on all the repositories on scheduled tasks. The design should allow for blind health checks regardless of the repository type. Requirement : For the design to allow this, there should be some high-level abstraction of all the repositories in the system. This abstraction layer should expose as most as possible of the common APIs of all the repositories. Design APIs Throttling Restrictions on Queries : All queries should allow to apply some variable throttling restrictions to control the amount of data retrieved per call. The throttling criteria could be implemented as run-time logic instead of some hardcoded thresholds. Additionally, the caller should be informed about this and the way to get the rest of the data if possible. Requirement : For the design to allow this, we should implement Paging to control the amount of data retrieved per call using the throttling logic. Also, some unified return object should be returned to the caller to inform him about the paging and how to get to the next page,… Design Disclaimer Some best practices would be ignored/dropped in order to drive the main focus to the other best practices targeted in this article. All the code could be found on so that you can easily follow it. this repository Some details of the client and the nature of the project were intentionally not revealed as they are confidential. Some code implementation exists for demonstration purposes only and I don’t advise using it or mimicking it on production code. On such kind of occasions, a comment would be provided. Some understanding of other topics and concepts would be required to be able to fully understand the code and design in this article. In such cases, a note -and maybe a link- would be provided. The paging implementation used in our example is specific for applying paging on memory lists. It is not intended to be used with production code that is using Entity Framework or any other frameworks. Please use the code on this article as a kickstarter or mind opener. Before going to production, you need to revise the design and adapt it to your own needs. Better Enhanced Implementation and Analysis Entities We would start with a very simple entity representing an employee. However, since our design in general would need some high-level abstraction, we need to have an abstract Entity that would be inherited by all our system entities. Entity namespace BetterRepository.Entities { public abstract class Entity { } } For our solution, we will keep it simple and don’t provide any common members for the abstract class. However, for your own solution, you might need to do so. Entity Employee using System; using System.Text; namespace BetterRepository.Entities { public class Employee : Entity { public int Id { get; } public string Name { get; } public Employee(int id, string name) { Id = id; Name = name; } public Employee(Employee other, int id) { if (other == null) throw new ArgumentException("Other cannot be null"); Id = id; Name = other.Name; } // This code is added for demonstration purposes only. public override string ToString() { var builder = new StringBuilder(); builder.AppendLine($"Id: {Id}, Name: {Name}"); return builder.ToString(); } } } What we can notice here: This is a simple class which inherits the abstract class. Employee Entity It has and . Id Name It is immutable and that’s why we have a constructor to copy all members -except the Id- from another Employee. We have an override of the method to be used for demonstration purposes only. . ToString This is not a best practice to follow Paging If you wish to understand the paging code, I recommend that you first go and check that article. I would include the paging code here for brevity and as a quick reference. using System; using System.Collections; using System.Linq; using System.Text; namespace BetterRepository.Models { public class PagingDescriptor { public int ActualPageSize { get; private set; } public int NumberOfPages { get; private set; } public PageBoundry[] PagesBoundries { get; private set; } public PagingDescriptor( int actualPageSize, int numberOfPages, PageBoundry[] pagesBoundries) { ActualPageSize = actualPageSize; NumberOfPages = numberOfPages; PagesBoundries = pagesBoundries; } // This code is added for demonstration purposes only. public override string ToString() { var builder = new StringBuilder(); builder.AppendLine($"ActualPageSize: {ActualPageSize}"); builder.AppendLine($"NumberOfPages: {NumberOfPages}"); builder.AppendLine(""); builder.AppendLine($"PagesBoundries:"); builder.AppendLine($"----------------"); foreach (var boundry in PagesBoundries) { builder.Append(boundry.ToString()); } return builder.ToString(); } } public class PageBoundry { public int FirstItemZeroIndex { get; private set; } public int LastItemZeroIndex { get; private set; } public PageBoundry(int firstItemZeroIndex, int lastItemZeroIndex) { FirstItemZeroIndex = firstItemZeroIndex; LastItemZeroIndex = lastItemZeroIndex; } // This code is added for demonstration purposes only. public override string ToString() { var builder = new StringBuilder(); builder.AppendLine($"FirstItemZeroIndex: {FirstItemZeroIndex}, LastItemZeroIndex: {LastItemZeroIndex}"); return builder.ToString(); } } public static class ListExtensionMethods { public static PagingDescriptor Page(this IList list, int pageSize) { var actualPageSize = pageSize; if (actualPageSize <= 0) { actualPageSize = list.Count; } var maxNumberOfPages = (int)Math.Round(Math.Max(1, Math.Ceiling(((float)list.Count) / ((float)actualPageSize)))); return new PagingDescriptor( actualPageSize, maxNumberOfPages, Enumerable .Range(0, maxNumberOfPages) .Select(pageZeroIndex => new PageBoundry( pageZeroIndex * actualPageSize, Math.Min((pageZeroIndex * actualPageSize) + (actualPageSize - 1), list.Count - 1) )).ToArray() ); } } } What we can notice here: This is the same code explained on the other article about Paging. The only added parts are about overriding the method. ToString This is done for demonstration purposes only. . This is not a best practice to follow QueryResult This leads us to the unified object which is returned from the Get repository calls which is in our case called . QueryResult using System.Collections.Generic; using System.Text; using BetterRepository.Entities; namespace BetterRepository.Models { public interface IQueryResult { PagingDescriptor PagingDescriptor { get; } int ActualPageZeroIndex { get; } IEnumerable<Entity> Results { get; } } public interface IQueryResult<out TEntity> : IQueryResult where TEntity : Entity { new IEnumerable<TEntity> Results { get; } } public class QueryResult<TEntity> : IQueryResult<TEntity> where TEntity : Entity { public QueryResult( PagingDescriptor pagingDescriptor, int actualPageZeroIndex, IEnumerable<TEntity> results) { PagingDescriptor = pagingDescriptor; ActualPageZeroIndex = actualPageZeroIndex; Results = results; } public PagingDescriptor PagingDescriptor { get; } public int ActualPageZeroIndex { get; } public IEnumerable<TEntity> Results { get; } IEnumerable<Entity> IQueryResult.Results => Results; // This code is added for demonstration purposes only. public override string ToString() { var builder = new StringBuilder(); builder.AppendLine($"ActualPageZeroIndex: {ActualPageZeroIndex}"); builder.AppendLine(""); builder.AppendLine($"PagingDescriptor:"); builder.AppendLine($"----------------"); builder.AppendLine(PagingDescriptor.ToString()); builder.AppendLine($"Results:"); builder.AppendLine($"----------------"); foreach (var entity in Results) { builder.Append(entity.ToString()); } return builder.ToString(); } } } What we can notice here: We have the interface which represents the object to be returned from the repository calls. IQueryResult Get It has and properties so that the caller would know the exact paging details of the data retrieved by his call. PagingDescriptor ActualPageZeroIndex And, the property which is an of the abstract base results. Results IEnumerable Entity We defined the generic interface so that we can have strong typed language support for our specific repositories. IQueryResult<out TEntity> On this interface, we are hiding the property and replacing it with a new typed one. IEnumerable<Entity> IQueryResult.Results Then comes the class which implements the interface. QueryResult<TEntity> IQueryResult<TEntity> We have an override of the method to be used for demonstration purposes only. . ToString This is not a best practice to follow AddOrUpdateDescriptor We would support different methods on our repositories including methods. This would make the caller life easier whenever needed. AddOrUpdate For that, we need to define a unified object structure to be returned from the method. AddOrUpdate namespace BetterRepository.Models { public enum AddOrUpdate { Add, Update } public interface IAddOrUpdateDescriptor { AddOrUpdate ActionType { get; } int Id { get; } } public class AddOrUpdateDescriptor : IAddOrUpdateDescriptor { public AddOrUpdate ActionType { get; } public int Id { get; } public AddOrUpdateDescriptor(AddOrUpdate actionType, int id) { ActionType = actionType; Id = id; } } } What we can notice here: We defined the interface. IAddOrUpdateDescriptor Also defined the class which implements and it is immutable. AddOrUpdateDescriptor IAddOrUpdateDescriptor Also defined the enum . AddOrUpdate Query Repository Now we move to our repository definitions. We would split our repository methods into two parts; and . I can hear you saying . Queries Commands Command and Query Responsibility Segregation (CQRS) Yes, our design would embrace the same concept of CQRS but it is not fully implemented here. So, please don’t judge this design as an incomplete CQRS as it was not intended to be in the first place. IQueryRepository public interface IQueryRepository { IQueryResult<Entity> GetAll(); Entity Get(int id); IQueryResult<Entity> Get(int pageSize, int pageIndex); } What we can notice here: This is our interface which represents any non-generic . IQueryRepository Query Repository We defined method to get an entity by Id. Entity Get(int id) We defined method to get all the entities inside the implementing repository. Please keep in mind that throttling restrictions should be applied and that’s why we are not just returning a list of entities, we are returning . IQueryResult<Entity> GetAll() IQueryResult We defined method to get entities when divided into certain page size. Please keep in mind that throttling restrictions should be applied here as well. Therefore, if the throttling threshold is somehow set to 5, and the caller is requesting to get entities in pages of 8 entities per page, an adaptation would be applied and the caller would be aware of it by using the returned object. IQueryResult<Entity> Get(int pageSize, int pageIndex) IQueryResult IQueryRepository<TEntity> public interface IQueryRepository<TEntity> : IQueryRepository where TEntity : Entity { new IQueryResult<TEntity> GetAll(); new TEntity Get(int id); new IQueryResult<TEntity> Get(int pageSize, int pageIndex); IQueryResult<TEntity> GetByExpression(Expression<Func<TEntity, bool>> predicate); IQueryResult<TEntity> GetByExpression(Expression<Func<TEntity, bool>> predicate, int pageSize, int pageIndex); } We defined method to get entities after filtering them using the passed in predicate. Please keep in mind that throttling restrictions should be applied here as well. IQueryResult<TEntity> GetByExpression(Expression<Func<TEntity, bool>> predicate) Additionally, we defined method to apply paging after filtering the entities using the passed in predicate. Please keep in mind that throttling restrictions should be applied here as well. IQueryResult<TEntity> GetByExpression(Expression<Func<TEntity, bool>> predicate, int pageSize, int pageIndex) QueryRepository<TEntity> public abstract class QueryRepository<TEntity> : IQueryRepository<TEntity> where TEntity : Entity { public abstract IQueryResult<TEntity> GetAll(); public abstract IQueryResult<TEntity> Get(int pageSize, int pageIndex); public abstract TEntity Get(int id); public abstract IQueryResult<TEntity> GetByExpression(Expression<Func<TEntity, bool>> predicate); public abstract IQueryResult<TEntity> GetByExpression(Expression<Func<TEntity, bool>> predicate, int pageSize, int pageIndex); IQueryResult<Entity> IQueryRepository.GetAll() { return GetAll(); } Entity IQueryRepository.Get(int id) { return Get(id); } IQueryResult<Entity> IQueryRepository.Get(int pageSize, int pageIndex) { return Get(pageSize, pageIndex); } } What we can notice here: This is an abstract class implementing . IQueryRepository<TEntity> The implementation of all the methods coming from the interface would be delegated to the child classes inheriting from . Get IQueryRepository<TEntity> QueryRepository<TEntity> For , it is defaulted to call the other which would be implemented by child classes. IQueryResult<Entity> IQueryRepository.GetAll() IQueryResult<TEntity> GetAll() And for , it is defaulted to call the other which would be implemented by child classes. Entity IQueryRepository.Get(int id) TEntity Get(int id) And for , it is defaulted to call the other which would be implemented by child classes. IQueryResult<Entity> IQueryRepository.Get(int pageSize, int pageIndex) IQueryResult<TEntity> Get(int pageSize, int pageIndex) Command Repository Here are the definitions related to any Command Repository. ICommandRepository public interface ICommandRepository { int Add(Entity entity); IEnumerable<int> Add(IEnumerable<Entity> entities); void Update(Entity entity); void Update(IEnumerable<Entity> entities); IAddOrUpdateDescriptor AddOrUpdate(Entity entity); IEnumerable<IAddOrUpdateDescriptor> AddOrUpdate(IEnumerable<Entity> entities); bool Delete(int id); bool Delete(Entity entity); IDictionary<int, bool> Delete(IEnumerable<Entity> entities); } What we can notice here: This is our interface which represents any non-generic . ICommandRepository Command Repository We defined all the required methods to interact with our repository. Methods like , , , and . Add Update AddOrUpdate Delete ICommandRepository<in TEntity> public interface ICommandRepository<in TEntity> : ICommandRepository where TEntity : Entity { int Add(TEntity entity); IEnumerable<int> Add(IEnumerable<TEntity> entities); void Update(TEntity entity); void Update(IEnumerable<TEntity> entities); IAddOrUpdateDescriptor AddOrUpdate(TEntity entity); IEnumerable<IAddOrUpdateDescriptor> AddOrUpdate(IEnumerable<TEntity> entities); bool Delete(TEntity entity); IDictionary<int, bool> Delete(IEnumerable<TEntity> entities); abstract int ICommandRepository.Add(Entity entity); abstract IEnumerable<int> ICommandRepository.Add(IEnumerable<Entity> entities); abstract void ICommandRepository.Update(Entity entity); abstract void ICommandRepository.Update(IEnumerable<Entity> entities); abstract IAddOrUpdateDescriptor ICommandRepository.AddOrUpdate(Entity entity); abstract IEnumerable<IAddOrUpdateDescriptor> ICommandRepository.AddOrUpdate(IEnumerable<Entity> entities); abstract bool ICommandRepository.Delete(Entity entity); abstract IDictionary<int, bool> ICommandRepository.Delete(IEnumerable<Entity> entities); } What worth mentioning here is that starting from you can add this to an interface. C#8.0, abstract int ICommandRepository.Add(Entity entity); In our case, this means that although extends , any class implementing interface would not expose method unless it is casted -implicitly or explicitly- into the non-generic interface. ICommandRepository<in TEntity> ICommandRepository ICommandRepository<in TEntity> int Add(Entity entity) ICommandRepository CommandRepository<TEntity> public abstract class CommandRepository<TEntity> : ICommandRepository<TEntity> where TEntity : Entity { public abstract bool Delete(int id); public abstract int Add(TEntity entity); public abstract IEnumerable<int> Add(IEnumerable<TEntity> entities); public abstract void Update(TEntity entity); public abstract void Update(IEnumerable<TEntity> entities); public abstract IAddOrUpdateDescriptor AddOrUpdate(TEntity entity); public abstract IEnumerable<IAddOrUpdateDescriptor> AddOrUpdate(IEnumerable<TEntity> entities); public abstract bool Delete(TEntity entity); public abstract IDictionary<int, bool> Delete(IEnumerable<TEntity> entities); int ICommandRepository.Add(Entity entity) { if (entity.GetType() == typeof(TEntity)) { return Add(entity as TEntity); } throw new ArgumentException( $"The type \"{entity.GetType()}\" does not match the type \"{typeof(TEntity)}\""); } IEnumerable<int> ICommandRepository.Add(IEnumerable<Entity> entities) { if (entities is IEnumerable<TEntity>) { return Add(entities.Select(e => e as TEntity)); } throw new ArgumentException( $"The type \"{entities.GetType()}\" does not match the type \"{typeof(IEnumerable<TEntity>)}\""); } void ICommandRepository.Update(Entity entity) { if (entity.GetType() == typeof(TEntity)) { Update(entity as TEntity); } else { throw new ArgumentException( $"The type \"{entity.GetType()}\" does not match the type \"{typeof(TEntity)}\""); } } void ICommandRepository.Update(IEnumerable<Entity> entities) { if (entities is IEnumerable<TEntity>) { Update(entities.Select(e => e as TEntity)); } else { throw new ArgumentException( $"The type \"{entities.GetType()}\" does not match the type \"{typeof(IEnumerable<TEntity>)}\""); } } IAddOrUpdateDescriptor ICommandRepository.AddOrUpdate(Entity entity) { if (entity.GetType() == typeof(TEntity)) { return AddOrUpdate(entity as TEntity); } throw new ArgumentException( $"The type \"{entity.GetType()}\" does not match the type \"{typeof(TEntity)}\""); } IEnumerable<IAddOrUpdateDescriptor> ICommandRepository.AddOrUpdate(IEnumerable<Entity> entities) { if (entities is IEnumerable<TEntity>) { return AddOrUpdate(entities.Select(e => e as TEntity)); } throw new ArgumentException( $"The type \"{entities.GetType()}\" does not match the type \"{typeof(IEnumerable<TEntity>)}\""); } bool ICommandRepository.Delete(Entity entity) { if (entity.GetType() == typeof(TEntity)) { return Delete(entity as TEntity); } throw new ArgumentException( $"The type \"{entity.GetType()}\" does not match the type \"{typeof(TEntity)}\""); } IDictionary<int, bool> ICommandRepository.Delete(IEnumerable<Entity> entities) { if (entities is IEnumerable<TEntity>) { return Delete(entities.Select(e => e as TEntity)); } throw new ArgumentException( $"The type \"{entities.GetType()}\" does not match the type \"{typeof(IEnumerable<TEntity>)}\""); } } What we can notice here: This is an abstract class implementing . ICommandRepository<TEntity> The implementation of all the methods coming from the interface would be delegated to the child classes inheriting from . ICommandRepository<TEntity> CommandRepository<TEntity> For , it is defaulted to call the other which would be implemented by child classes, however, we added an extra check on the passed in type to make sure it matches the expected one. int ICommandRepository.Add(Entity entity) int Add(TEntity entity) And for , it is defaulted to call the other which would be implemented by child classes, however, we added an extra check on the passed in type to make sure it matches the expected one. IEnumerable<int> ICommandRepository.Add(IEnumerable<Entity> entities) IEnumerable<int> Add(IEnumerable<TEntity> entities) And for , it is defaulted to call the other which would be implemented by child classes, however, we added an extra check on the passed in type to make sure it matches the expected one. void ICommandRepository.Update(Entity entity) void Update(TEntity entity) And for , it is defaulted to call the other which would be implemented by child classes, however, we added an extra check on the passed in type to make sure it matches the expected one. void ICommandRepository.Update(IEnumerable<Entity> entities) void Update(IEnumerable<TEntity> entities) And for , it is defaulted to call the other which would be implemented by child classes, however, we added an extra check on the passed in type to make sure it matches the expected one. IAddOrUpdateDescriptor ICommandRepository.AddOrUpdate(Entity entity) IAddOrUpdateDescriptor AddOrUpdate(TEntity entity) And for , it is defaulted to call the other which would be implemented by child classes, however, we added an extra check on the passed in type to make sure it matches the expected one. IEnumerable<IAddOrUpdateDescriptor> ICommandRepository.AddOrUpdate(IEnumerable<Entity> entities) IEnumerable<IAddOrUpdateDescriptor> AddOrUpdate(IEnumerable<TEntity> entities) And for , it is defaulted to call the other which would be implemented by child classes, however, we added an extra check on the passed in type to make sure it matches the expected one. bool ICommandRepository.Delete(Entity entity) bool Delete(TEntity entity) And for , it is defaulted to call the other which would be implemented by child classes, however, we added an extra check on the passed in type to make sure it matches the expected one. IDictionary<int, bool> ICommandRepository.Delete(IEnumerable<Entity> entities) IDictionary<int, bool> Delete(IEnumerable<TEntity> entities) Employee Query and Command Repositories Now, this is the time to implement our Employee and repositories. This is where we start inheriting from our base abstract and classes. Query Command CommandRepository<TEntity> QueryRepository<TEntity> However, first, let’s clarify that for our example here we are not going to use a real database for storing our Employees data. What we are going to do instead, is to create a static list. EmployeePersistence internal static class EmployeePersistence { public static readonly List<Employee> Employees = new(); static EmployeePersistence() { Reset(); } public static void Reset() { Employees.Clear(); Employees.Add(new Employee(0, "Ahmed")); Employees.Add(new Employee(1, "Tarek")); Employees.Add(new Employee(2, "Patrick")); Employees.Add(new Employee(3, "Mohamed")); Employees.Add(new Employee(4, "Sara")); Employees.Add(new Employee(5, "Ali")); } } It is as simple as a list of employees. EmployeeQueryRepository public class EmployeeQueryRepository : QueryRepository<Employee> { private static int MaxResultsCountPerPage = 5; public override IQueryResult<Employee> GetAll() { return Get(emp => true, null, null); } public override Employee Get(int id) { return EmployeePersistence.Employees.FirstOrDefault(e => e.Id == id); } public override IQueryResult<Employee> Get(int pageSize, int pageIndex) { return Get(emp => true, pageSize, pageIndex); } public override IQueryResult<Employee> GetByExpression(Expression<Func<Employee, bool>> predicate) { return Get(predicate, null, null); } public override IQueryResult<Employee> GetByExpression(Expression<Func<Employee, bool>> predicate, int pageSize, int pageIndex) { return Get(predicate, pageSize, pageIndex); } private static IQueryResult<Employee> Get(Func<Employee, bool> predicate, int? pageSize, int? pageIndex) { var filteredItems = predicate != null ? EmployeePersistence.Employees.AsQueryable().Where(predicate).ToList() : EmployeePersistence.Employees; var finalPageSize = Math.Min(MaxResultsCountPerPage, filteredItems.Count); var finalPageIndex = 0; if (pageSize != null) { if (pageSize <= MaxResultsCountPerPage) { finalPageSize = pageSize.Value; finalPageIndex = pageIndex ?? 0; } else { finalPageSize = MaxResultsCountPerPage; if (pageIndex != null) { var oldPagingDescriptor = filteredItems.Page(pageSize.Value); var oldPageBoundries = oldPagingDescriptor.PagesBoundries[pageIndex.Value]; var targetedItemZeroIndex = oldPageBoundries.FirstItemZeroIndex; var newPagingDescriptor = filteredItems.Page(finalPageSize); finalPageIndex = newPagingDescriptor .PagesBoundries .ToList() .FindIndex(i => i.FirstItemZeroIndex <= targetedItemZeroIndex && i.LastItemZeroIndex >= targetedItemZeroIndex); } } } var pagingDescriptor = filteredItems.Page(finalPageSize); var pageBoundries = pagingDescriptor.PagesBoundries[finalPageIndex]; var from = pageBoundries.FirstItemZeroIndex; var to = pageBoundries.LastItemZeroIndex; return new QueryResult<Employee>(pagingDescriptor, finalPageIndex, filteredItems.Skip(from).Take(to - from + 1)); } } What we can notice here: The class is inheriting from . EmployeeQueryRepository QueryRepository<Employee> We don’t have to implement the methods coming from the non-generic interface as they have been implemented in the abstract base class. IQueryRepository QueryRepository<TEntity> All the methods are finally calling the centralized logic in the method where the magic happens. Get IQueryResult<Employee> Get(Func<Employee, bool> predicate, int? pageSize, int? pageIndex) On this method, the throttling is happening using the static and the extension method we implemented on the section. MaxResultsCountPerPage Page Paging EmployeeCommandRepository public class EmployeeCommandRepository : CommandRepository<Employee> { public override int Add(Employee entity) { var newEmployee = new Employee(entity, EmployeePersistence.Employees.Count); EmployeePersistence.Employees.Add(newEmployee); return newEmployee.Id; } public override IEnumerable<int> Add(IEnumerable<Employee> entities) { return entities.Select(Add).ToList(); } public override void Update(Employee entity) { var foundIndex = EmployeePersistence.Employees.FindIndex(e => e.Id == entity.Id); if (foundIndex == -1) { throw new InvalidOperationException($"Employee with Id \"{entity.Id}\" does not exist."); } var foundEmployee = EmployeePersistence.Employees[foundIndex]; var newEmployee = new Employee(entity, foundEmployee.Id); EmployeePersistence.Employees.RemoveAt(foundIndex); EmployeePersistence.Employees.Insert(foundIndex, newEmployee); } public override void Update(IEnumerable<Employee> entities) { foreach (var employee in entities) { Update(employee); } } public override IAddOrUpdateDescriptor AddOrUpdate(Employee entity) { var foundIndex = EmployeePersistence.Employees.FindIndex(e => e.Id == entity.Id); if (foundIndex != -1) { Update(entity); return new AddOrUpdateDescriptor(Models.AddOrUpdate.Update, entity.Id); } else { return new AddOrUpdateDescriptor(Models.AddOrUpdate.Add, Add(entity)); } } public override IEnumerable<IAddOrUpdateDescriptor> AddOrUpdate(IEnumerable<Employee> entities) { return entities.Select(AddOrUpdate).ToList(); } public override bool Delete(int id) { var foundIndex = EmployeePersistence.Employees.FindIndex(e => e.Id == id); if (foundIndex == -1) return false; EmployeePersistence.Employees.RemoveAt(foundIndex); return true; } public override bool Delete(Employee entity) { return Delete(entity.Id); } public override IDictionary<int, bool> Delete(IEnumerable<Employee> entities) { return entities .ToDictionary(emp => emp.Id, Delete); } } What we can notice here: The class is inheriting from . EmployeeCommandRepository CommandRepository<Employee> We don’t have to implement the methods coming from the non-generic interface as they have been implemented in the abstract base class. ICommandRepository CommandRepository<TEntity> The methods are simply implemented to use the static employees list. Moment of Truth Now, it is time to test our design. I created a Console Application for quick testing. Testing would be divided into three parts: Demonstrating Basic Operations. Demonstrating Wrong Casting Checks. Demonstrating Dealing With Abstractions. Therefore, we will end up with a class as follows: Program using System; using BetterRepository.Entities; using BetterRepository.Models; using BetterRepository.Repositories; namespace BetterRepository { internal class Program { private static readonly EmployeeQueryRepository m_EmployeeQueryRepository = new(); private static readonly EmployeeCommandRepository m_EmployeeCommandRepository = new(); static void Main(string[] args) { DemonstratingBasicOperation(); DemonstratingWrongCastingChecks(); DemonstratingDealingWithAbstractions(); Console.ReadLine(); } } public class Student : Entity { } } We also defined a class which inherits from Entity and we are going to use it for testing at some point. Student Now, we move to the implementation of the , , and one by one. DemonstratingBasicOperation() DemonstratingWrongCastingChecks() DemonstratingDealingWithAbstractions() Demonstrating Basic Operations public static void DemonstratingBasicOperation() { Console.WriteLine("Started DemonstratingBasicOperation"); var result = m_EmployeeQueryRepository.GetAll(); Console.WriteLine(result); /* ActualPageZeroIndex: 0 PagingDescriptor: ---------------- ActualPageSize: 5 NumberOfPages: 2 PagesBoundries: ---------------- FirstItemZeroIndex: 0, LastItemZeroIndex: 4 FirstItemZeroIndex: 5, LastItemZeroIndex: 5 Results: ---------------- Id: 0, Name: Ahmed Id: 1, Name: Tarek Id: 2, Name: Patrick Id: 3, Name: Mohamed Id: 4, Name: Sara */ result = m_EmployeeQueryRepository.Get(result.PagingDescriptor.ActualPageSize, result.ActualPageZeroIndex + 1); Console.WriteLine(result); /* ActualPageZeroIndex: 1 PagingDescriptor: ---------------- ActualPageSize: 5 NumberOfPages: 2 PagesBoundries: ---------------- FirstItemZeroIndex: 0, LastItemZeroIndex: 4 FirstItemZeroIndex: 5, LastItemZeroIndex: 5 Results: ---------------- Id: 5, Name: Ali */ result = m_EmployeeQueryRepository.Get(6, 0); Console.WriteLine(result); /* ActualPageZeroIndex: 0 PagingDescriptor: ---------------- ActualPageSize: 5 NumberOfPages: 2 PagesBoundries: ---------------- FirstItemZeroIndex: 0, LastItemZeroIndex: 4 FirstItemZeroIndex: 5, LastItemZeroIndex: 5 Results: ---------------- Id: 0, Name: Ahmed Id: 1, Name: Tarek Id: 2, Name: Patrick Id: 3, Name: Mohamed Id: 4, Name: Sara */ var tarek = m_EmployeeQueryRepository.Get(2); Console.WriteLine(tarek); /* Id: 1, Name: Tarek */ result = m_EmployeeQueryRepository.GetByExpression(emp => emp.Name.ToLower().Contains("t")); Console.WriteLine(result); /* ActualPageZeroIndex: 0 PagingDescriptor: ---------------- ActualPageSize: 2 NumberOfPages: 1 PagesBoundries: ---------------- FirstItemZeroIndex: 0, LastItemZeroIndex: 1 Results: ---------------- Id: 1, Name: Tarek Id: 2, Name: Patrick */ var erikId = m_EmployeeCommandRepository.Add(new Employee(0, "Erik")); Console.WriteLine(erikId); /* 6 */ var added = m_EmployeeCommandRepository.Add(new Employee[] { new Employee(0, "Hasan"), new Employee(0, "Mai"), new Employee(0, "John") }); Console.WriteLine(""); Console.WriteLine(String.Join("\r\n", added)); /* 7 8 9 */ m_EmployeeCommandRepository.Update(new Employee(1, "Tarek - Updated")); var tarekUpdated = m_EmployeeQueryRepository.Get(1); Console.WriteLine(""); Console.WriteLine(tarekUpdated); /* Id: 1, Name: Tarek - Updated */ m_EmployeeCommandRepository.AddOrUpdate(new Employee(1, "Tarek - Updated - Updated")); var tarekUpdatedUpdated = m_EmployeeQueryRepository.Get(1); Console.WriteLine(""); Console.WriteLine(tarekUpdatedUpdated); /* Id: 1, Name: Tarek - Updated - Updated */ var deletedTarek = m_EmployeeCommandRepository.Delete(1); Console.WriteLine(""); Console.WriteLine(deletedTarek); /* True */ var checkTarek = m_EmployeeQueryRepository.Get(1); Console.WriteLine(""); Console.WriteLine(checkTarek != null); /* False */ Console.WriteLine("Finished DemonstratingBasicOperation"); } We are calling followed by and the result is: var result = m_EmployeeQueryRepository.GetAll(); Console.WriteLine(result); ActualPageZeroIndex: 0 PagingDescriptor: — — — — — — — — ActualPageSize: 5 NumberOfPages: 2 PagesBoundries: — — — — — — — — FirstItemZeroIndex: 0, LastItemZeroIndex: 4 FirstItemZeroIndex: 5, LastItemZeroIndex: 5 Results: — — — — — — — — Id: 0, Name: Ahmed Id: 1, Name: Tarek Id: 2, Name: Patrick Id: 3, Name: Mohamed Id: 4, Name: Sara So, the is working fine. Get All Then we are calling followed by and the result is: result = m_EmployeeQueryRepository.Get(result.PagingDescriptor.ActualPageSize, result.ActualPageZeroIndex + 1); Console.WriteLine(result); ActualPageZeroIndex: 1 PagingDescriptor: — — — — — — — — ActualPageSize: 5 NumberOfPages: 2 PagesBoundries: — — — — — — — — FirstItemZeroIndex: 0, LastItemZeroIndex: 4 FirstItemZeroIndex: 5, LastItemZeroIndex: 5 Results: — — — — — — — — Id: 5, Name: Ali So, the is working fine. Get With Paging Then we are calling followed by and the result is: result = m_EmployeeQueryRepository.Get(6, 0); Console.WriteLine(result); ActualPageZeroIndex: 0 PagingDescriptor: — — — — — — — — ActualPageSize: 5 NumberOfPages: 2 PagesBoundries: — — — — — — — — FirstItemZeroIndex: 0, LastItemZeroIndex: 4 FirstItemZeroIndex: 5, LastItemZeroIndex: 5 Results: — — — — — — — — Id: 0, Name: Ahmed Id: 1, Name: Tarek Id: 2, Name: Patrick Id: 3, Name: Mohamed Id: 4, Name: Sara Here we need to keep in mind the applied throttling restriction of items. Analyzing this, we would get convinced that the is working fine. 5 Get With Paging Then we are calling followed by and the result is: var tarek = m_EmployeeQueryRepository.Get(2); Console.WriteLine(tarek); Id: 1, Name: Tarek So, the is working fine. Get By Id Then we are calling followed by and the result is: result = m_EmployeeQueryRepository.Get(emp => emp.Name.ToLower().Contains(“t”)); Console.WriteLine(result); ActualPageZeroIndex: 0 PagingDescriptor: — — — — — — — — ActualPageSize: 2 NumberOfPages: 1 PagesBoundries: — — — — — — — — FirstItemZeroIndex: 0, LastItemZeroIndex: 1 Results: — — — — — — — — Id: 1, Name: Tarek Id: 2, Name: Patrick So, the is working fine. Get With Predicate Filter Then we are calling followed by and the result is: var erikId = m_EmployeeCommandRepository.Add(new Employee(0, “Erik”)); Console.WriteLine(erikId); 6 So, the is working fine. Add Then we are calling var added = m_EmployeeCommandRepository.Add(new [] { new Employee(0, “Hasan”), new Employee(0, “Mai”), new Employee(0, “John”) }); followed by and the result is: Console.WriteLine(String.Join(“\r\n”, added)); 7 8 9 So, the is working fine. Add Collection Then we are calling m_EmployeeCommandRepository.Update(new Employee(1, “Tarek — Updated”)); var tarekUpdated = m_EmployeeQueryRepository.Get(1); followed by and the result is: Console.WriteLine(tarekUpdated); Id: 1, Name: Tarek — Updated So, the is working fine. Update Then we are calling m_EmployeeCommandRepository.AddOrUpdate(new Employee(1, “Tarek — Updated — Updated”)); var tarekUpdatedUpdated = m_EmployeeQueryRepository.Get(1); followed by and the result is: Console.WriteLine(tarekUpdatedUpdated); Id: 1, Name: Tarek — Updated — Updated So, the is working fine. Add Or Update Then we are calling followed by and the result is: var deletedTarek = m_EmployeeCommandRepository.Delete(1); Console.WriteLine(deletedTarek); True And calling followed by and the result is: var checkTarek = m_EmployeeQueryRepository.Get(1); Console.WriteLine(checkTarek != null); False So, the is working fine. Delete Demonstrating Wrong Casting Checks public static void DemonstratingWrongCastingChecks() { Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("Started DemonstratingWrongCastingChecks"); var queryRepository = m_EmployeeQueryRepository as IQueryRepository; var commandRepository = m_EmployeeCommandRepository as ICommandRepository; try { commandRepository.Add(new Student()); } catch (Exception e) { Console.WriteLine(e.Message); /* System.ArgumentException: 'The type "BetterRepository.Student" does not match the type "BetterRepository.Entities.Employee"' */ } try { commandRepository.Add(new Student[] { new Student(), new Student() }); } catch (Exception e) { Console.WriteLine(e.Message); /* System.ArgumentException: The type "BetterRepository.Student[]" does not match the type "System.Collections.Generic.IEnumerable`1[BetterRepository.Entities.Employee]" */ } try { bool FilterStudents(object obj) { return true; } // Compiler would not allow it as Func<Entity, bool> predicate is contravariant // This means that for the predicate parameter, you can only provide Func<Entity, bool> // or Func<Parent of Entity, bool>. In our case, the only available parent of Entity is // the Object class. //queryRepository.Get(FilterStudents); } catch (Exception e) { Console.WriteLine(e.Message); } Console.WriteLine("Finished DemonstratingWrongCastingChecks"); } We are defining the local variable which is of type by casting . queryRepository IQueryRepository m_EmployeeQueryRepository var queryRepository = m_EmployeeQueryRepository as IQueryRepository; And defining the local variable which is of type by casting . commandRepository ICommandRepository m_EmployeeCommandRepository var commandRepository = m_EmployeeCommandRepository as ICommandRepository; Then when trying to execute it fails with the exception . commandRepository.Add(new Student()); System.ArgumentException: ‘The type “BetterRepository.Student” does not match the type “BetterRepository.Entities.Employee”’ Then when trying to execute it fails with the exception . commandRepository.Add(new Student[] { new Student(), new Student() }); System.ArgumentException: The type “BetterRepository.Student[]” does not match the type “System.Collections.Generic.IEnumerable`1[BetterRepository.Entities.Employee]” So, the is working fine. Wrong Casting Check Demonstrating Dealing With Abstractions public static void DemonstratingDealingWithAbstractions() { Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("Started DemonstratingDealingWithAbstractions"); // Resetting the employees collection EmployeePersistence.Reset(); var queryRepository = m_EmployeeQueryRepository as IQueryRepository; var commandRepository = m_EmployeeCommandRepository as ICommandRepository; // Getting first two Employees when we actually don't know their type // and we don't care about their type var firstTwoItems = queryRepository.Get(2, 0); Console.WriteLine(firstTwoItems); /* Id: 0, Name: Ahmed Id: 1, Name: Tarek */ // Now we are deleting the first two items blindly when we don't know their type // and we don't care about their type commandRepository.Delete(firstTwoItems.Results); // Now we get the first two Employees again to check if it worked firstTwoItems = queryRepository.Get(2, 0); Console.WriteLine(firstTwoItems); /* Id: 2, Name: Patrick Id: 3, Name: Mohamed */ Console.WriteLine("Finished DemonstratingDealingWithAbstractions"); } First, we are resetting the Employees list . EmployeePersistence.Reset(); Then, we are defining the local variable which is of type by casting . queryRepository IQueryRepository m_EmployeeQueryRepository var queryRepository = m_EmployeeQueryRepository as IQueryRepository; And defining the local variable which is of type by casting . commandRepository ICommandRepository m_EmployeeCommandRepository var commandRepository = m_EmployeeCommandRepository as ICommandRepository; Then we execute: / Getting first two Employees when we actually don’t know their type // and we don’t care about their type var firstTwoItems = queryRepository.Get(2, 0); Console.WriteLine(firstTwoItems); And the result is: Id: 0, Name: Ahmed Id: 1, Name: Tarek And then we execute: // Now we are deleting the first two items blindly when we don’t know their type // and we don’t care about their type commandRepository.Delete(firstTwoItems.Results); // Now we get the first two Employees again to check if it worked firstTwoItems = queryRepository.Get(2, 0); Console.WriteLine(firstTwoItems); And the result is: Id: 2, Name: Patrick Id: 3, Name: Mohamed So, is working fine. Dealing With Abstractions Final Words Wow, a long trip. Now, let’s take a break, relax, and take a step back to see the big picture. What we implemented here is not rocket science. The concepts are not that hard, it is only a matter of having steady hands while implementing them. That’s it, hope you found reading this article as interesting as I found writing it. Also Published Here