Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use dependency injection with inheritance in C#

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!

like image 241
Maxime Gélinas Avatar asked Jan 01 '18 00:01

Maxime Gélinas


People also ask

Is inheritance a dependency?

Inheritance forces dependency between a superclass and its subclasses. But if this dependency is well designed, not only it is harmless, it could actually be useful and make code conceptually cohesive.

What is difference between inheritance and dependency injection?

One criticism of inheritance is that it tightly couples parent class with child class. It is harder to reuse the code and write unit tests. That's why most developers prefer dependency injection as a way to reuse code. Dependency injection is a way to inject dependencies into a class for use in its methods.

What is dependency injection with Example C?

Using dependency injection, we can pass an instance of class C to class B, and pass an instance of B to class A, instead of having these classes to construct the instances of B and C. In the example, below, class Runner has a dependency on the class Logger.

How do you inherit in C sharp?

The symbol used for inheritance is :. Syntax: class derived-class : base-class { // methods and fields . . } Example: In below example of inheritance, class GFG is a base class, class GeeksforGeeks is a derived class which extends GFG class and class Sudo is a driver class to run program.


1 Answers

After some years of experience, I found the Decorator Pattern a perfect fit for this.

Implementation:

// Abstract type
public interface IRepository<T>
{
    Add(T obj);
}

// Concete type
public class UserRepository : IRepository<User>
{
    public UserRepository(/* Specific dependencies */) {}

    Add(User obj) { /* [...] */ }
}

// Decorator
public class LoggingRepository<T> : IRepository<T>
{
    private readonly IRepository<T> _inner;

    public LoggingRepository<T>(IRepository<T> inner) => _inner = inner;

    Add(T obj) 
    {
        Console.Log($"Adding {obj}...");
        _inner.Add(obj);
        Console.Log($"{obj} addded.");
    }
}

Usage:

// Done using the DI.
IRepository<User> repository = 
    // Add as many decorators as you want.
    new LoggingRepository<User>(
        new UserRepository(/* [...] */));

// And here is your add method wrapped with some logging :)
repository.Add(new User());

This pattern is awesome, because you can encapsulate behaviors in separate classes without breaking changes and using them only when you really need them.

like image 172
Maxime Gélinas Avatar answered Sep 28 '22 16:09

Maxime Gélinas