Introduction
Hi everyone, I'm currently working on a persistence library in C#. In that library, I have implemented the repository pattern where I'm facing a SOLID issue. Here is a simplified example my current implementation to focus on the essential:
The abstract repository containing in the persistence library:
public abstract class Repository<T>
{
protected Repository(
IServiceA serviceA,
IServiceB serviceB)
{
/* ... */
}
}
The concrete repository created by the library user:
public class FooRepository : Repository<Foo>
{
protected FooRepository(
IServiceA serviceA,
IServiceB serviceB) :
base(serviceA, serviceB)
{
/* ... */
}
}
Problem
OK, with the current code, the derived class has to know every dependency of the base class which can be ok, but what if I add a dependency to the base class? Every derived class will break because they will need to pass that new dependency to the base class... So currently, I'm limited to never change the base class constructor and it's a problem because I want my base class to had the possibility to evolve. This implementation clearly breaks the Open/Closed Principle, but I don't know how to solve this issue without breaking the SOLID...
Requirements
- The library should be easy to use for the user
- The concrete repositories should be able to be constructed through the DI
- One or more dependencies should be added to the abstract repository without impacting the derived repositories
- It should be possible to register every repository in the DI container using a naming convention as does the ASP.NET MVC framework with the controllers
- The user should be able to add more dependencies in his derived repository if he wants
Solutions already envisaged
1. Service aggregator pattern
Following this article, the service aggregator model can be applied in this situation so the code would look like something like this:
The abstract repository containing in the persistence library:
public abstract class Repository<T>
{
public interface IRepositoryDependencies
{
IServiceA { get; }
IServiceB { get; }
}
protected Repository(IRepositoryDependencies dependencies)
{
/* ... */
}
}
The concrete repository created by the library user:
public class FooRepository : Repository<Foo>
{
protected Repository(IRepositoryDependencies dependencies) :
base(dependencies)
{
/* ... */
}
}
Pros
- The derived classes don't break if a dependency is added to the base class
Cons
- The implementation of the
IRepositoryDependencies
interface has to be modified if we add a dependency - The article doesn't explain how to use Castle DynamicProxy2 (which is an unknown technology for me) to dynamically generate the service aggregator
2. Builder pattern
Perhaps, it's possible to remove the base repository constructor and introduce a builder template to create the repositories, but for this solution to work, the builder must be inheritable to allow the user to enter his repository own dependencies.
Pros
- The derived classes don't break if a dependency is added to the base class
- The repositories construction is managed by another class
Cons
- The user has to create a builder for each repository he wants to create
- It's become harder to register every repository through the DI using a naming convention
3. Property injection
Perhaps removing the base repository constructor and configuring the DI to use property injection might be an option.
Pros
- The derived classes don't break if a dependency is added to the base class
Cons
- I think the property setter must be public?
Conclusion
Is there any of the mentioned solutions that could be acceptable in a SOLID world? If not, do you have a solution for me guys? You help is very appreciated!