16

In my asp.net core application I have dependency classes which are injected to almost all services. So I want to build a base service class to get these dependencies to properties and my services inherit this base service class.

public abstract class BaseService
{
    protected Foo Foo { get; set; }
    protected Bar Bar { get; set; }

    public BaseService(Foo foo, Bar bar)
    {
        Foo = foo;
        Bar = bar;
    }
}
public class Service : BaseService
{
    public Service(IOtherDependency otherDependency) { }

    public void Method()
    {
        var value = Bar.value;
        Foo.Do(value);
    }
}

So with the given code it warns me to call base constructor with supplied parameters, however they are the parameters that will be injected on runtime, I don't want it. If I add a parameterless constructor it will not call my parameterized constructor which I need.

I don't want to call or define any class that injected in base service(Foo and Bar) inside my inherited service, how can I do that ?

By the way Foo and Bar classes are injected as singleton to container in case their lifetime are important.

ibubi
  • 2,469
  • 3
  • 29
  • 50

4 Answers4

22

This is what I was looking for.


I modified my code for my controller base as indicated above post.

For my service side which I was asking in my question; as they do not make use of HttpContext like built-in Controller base class, I only inject IServiceProvider class to my BaseService, so whatever I need in all my services, I get it to property via provider.GetService().

public abstract class BaseService
{
    protected Foo Foo { get; set; }
    protected Bar Bar { get; set; }

    public BaseService(IServiceProvider provider)
    {
        Foo = provider.GetService<Foo>();
        Bar = provider.GetService<Bar>();
    }
}
public class Service : BaseService, IService
{
    public Service(IOtherDependency otherDependency, IServiceProvider provider) : base(provider) { }

    public void Method()
    {
        var value = Bar.value;
        Foo.Do(value);
    }
}

public class SomeController : BaseController
{
    private readonly IService _service;

    public SomeController(IService service)
    {
        _service = service;
    }

    public IActionResult Index()
    {
        //call method
        _service.Method();
    }
}
Shayan Shafiq
  • 1,447
  • 5
  • 18
  • 25
ibubi
  • 2,469
  • 3
  • 29
  • 50
  • How will you invoke class Service as it requires parameter for its constructor? – Amir Dec 10 '19 at 22:33
  • @Amir I do not directly invoke Services in controllers, they are called via dependency injection, with interface convention. – ibubi Dec 11 '19 at 05:23
  • So could you also provide code for binding. How you are binding your class and interface – Amir Dec 11 '19 at 14:28
  • 1
    This works well, thank you. But I'm curious if this is an anti-pattern. My understanding is that this resembles the Service Locator anti-pattern. If so, why is it an anti-pattern and what can we do about it to make it better? How I'm using it is, I have an abstract base class called **BaseVerifier** and numerous other classes implement this base class and provide their own business logic. They all need access to several services through DI, for example the **AppDbContext** and **UserManager**. I don't want to pass these arguments all over the place, so I resolve them in the base class. – Nexus Jul 13 '20 at 22:57
  • 1
    @Nexus I agree with you: though this solution works (and is the best I have right now) it itches... This solution exposes that is has a dependency (to the IServiceProvider) but hides the dependencies to Foo and Bar. Adding new dependencies to BaseService will definitely cause some serious debugging for any consumers of this class that does not have the source code (or is in another part of the enterprise). I think I'll solve my problem with the (meta) design pattern: Prefer composition over inheritance. – Quido Mar 04 '21 at 10:54
  • for generic method: using Microsoft.Extensions.DependencyInjection; – al.koval Sep 11 '21 at 12:00
8

Good. Let us have the base class BaseService with a constructor passing its dependencies as parameters:

public abstract class BaseService
{
    protected IFoo Foo { get; private set; }
    protected IBar Bar { get; private set; }

    public BaseService(IFoo foo, IBar bar)
    {
        Foo = foo;
        Bar = bar;
    }
}

public class Service : BaseService
{
    protected IOtherDependency otherDependency { get; private set; }

    public Service(IOtherDependency otherDependency, IFoo foo, IBar bar)
        : base(foo, bar)
    {
        OtherDependency = otherDependency;
    }

}

What can the developer of the BaseService class now do when they find out that some of its functionality should be outsourced to an external service on which that class should depend?

Adding another INewDependency parameter to the BaseService class constructor is a violation of the contract with the derived class developers, because they call the BaseService class constructor explicitly and expect only two parameters, so when upgrading to the new version, the corresponding signature constructor is not found and compilation fails. In my opinion, the requirement to repeat the list of base class dependencies in the code of derived classes is a violation of the Single Source Of Truth principle, and the compilation failure after allocating part of the base class functionality into the dependency is a consequence of this violation.

As a workaround, I suggest concentrating all the dependencies of the base class into the properties of the designated sealed class, register this class by ConfigureServices, and pass it in the constructor parameter.

Do the same for a derived class. The developer registers each of these classes with the IServicesCollection.

public abstract class BaseService
{
    protected IFoo Foo { get; private set; }
    protected IBar Bar { get; private set; }

    public BaseService(Dependencies dependencies)
    {
        Foo = dependencies.Foo;
        Bar = dependencies.Bar;
    }

    public sealed class Dependencies
    {
        internal IFoo Foo { get; private set; }
        internal IBar Bar { get; private set; }

        public Dependencies(IFoo foo, IBar bar)
        {
            Foo = foo;
            Bar = bar;
        }
    }
}

An object with parent class dependencies will be referenced by a class property that provides child class dependencies. However, the code of the derived class completely abstracts from the list of dependencies of the parent class, which is encapsulated in the BaseService.Dependencies type:

public class Service : BaseService
{
    protected IOtherDependency OtherDependency { get; private set; }

    public Service(Dependencies dependencies) : base(dependencies.BaseDependencies)
    {
        OtherDependency = dependencies.OtherDependency;
    }

    public new sealed class Dependencies
    {
        internal IOtherDependency OtherDependency { get; private set; }
        internal BaseService.Depencencies BaseDependencies { get; private set; }

        public Dependencies(IOtherDependency otherDependency, BaseService.Dependencies baseDependencies)
        {
            OtherDependency = otherDependency;
            BaseDependencies = baseDependencies;
        }
    }    
}

In this design, the constructor of each class has a single parameter, an instance of the sealed class with its dependencies, with inherited dependencies being passed as a property. The list of class dependencies is encapsulated in the Dependencies class, which is provided to consumers along with the class and default IServiceCollection registrations.

If a BaseClass developer decides to outsource some functionality to a new dependency, all necessary changes will be made within the supplied package, while its consumers do not have to change anything in their code.

Jan Jurníček
  • 126
  • 1
  • 5
  • nice, but I think a `sealed Dependencies` class in every derived class is too much. Just having one for `BaseClass` is enough for me. And now I realized this is actually what I always do in javascript to manage method parameters. wp – mkb Apr 17 '21 at 10:07
  • 1
    If the Services class is not marked as sealed, the situation may recur: The same or another developer may derive another class from it in the constructor of the derived class and will call the constructor of the Service class explicitly. Then, if someone finds that part of the functionality of the Service class should be outsourced to a dependency and changes the signature of its constructor, a problem occurs. In my opinion, the signatures of the class constructors should be standardized, unless the classes are sealed or very simple. – Jan Jurníček Apr 19 '21 at 18:13
4

Here is the simplest way by using the generic Base Controller class:

public abstract class BaseController<T> : Controller
{
    private IFoo _fooInstance;
    private IBar _barInstance;

    protected IFoo _foo => _fooInstance ??= HttpContext.RequestServices.GetService<IFoo>();
    protected IBar _bar => _barInstance ??= HttpContext.RequestServices.GetService<IBar>();
}

and if you are using Razor pages:

class BasePageModel<T> : PageModel where T : class
{
    private IFoo _fooInstance;
    private IBar _barInstance;

    protected IFoo _foo => _fooInstance ??= HttpContext.RequestServices.GetService<IFoo>();
    protected IBar _bar => _barInstance ??= HttpContext.RequestServices.GetService<IBar>();
}
Majid Shahabfar
  • 4,010
  • 2
  • 28
  • 36
3

You can't do it that way.

If you derive from a base class that does not have a default constructor then you must pass the parameters to the derived class constructor and call the base class constructor with them e.g.

public abstract class BaseService
{
    protected Foo Foo { get; set; }
    protected Bar Bar { get; set; }

    public BaseService(Foo foo, Bar bar)
    {
        Foo = foo;
        Bar = bar;
    }
}

public class Service : BaseService
{
    public Service(IOtherDependency otherDependency, Foo foo, Bar bar) : base(foo, bar) { }
}

The compiler wants to create the base class but needs the parameters to do so. Image the scenario if you have 2 constructors on your base class

public abstract class BaseService
{
    public BaseService(Foo foo)
    {
        ...
    }

    public BaseService(Bar bar)
    {
        ...
    }
}

In this scenario which base class constructor does the compiler call from the derived class? You need to be explicit with parameterized constructors so you need to pass them to any derived constructors as well.

Simply Ged
  • 8,250
  • 11
  • 32
  • 40
  • The above code is that what I have done so far. Actually I am asking that; _- Hey derived service! You do not pass any parameter to the base service, because base service will create via dependency injection and pass them to its properties for you._ So, you can offer another class structure or ways to achieve this. – ibubi Oct 25 '18 at 06:42
  • Sorry. It cannot be done - and it is bad practice to do so because you are hiding dependencies. See [this answer](https://stackoverflow.com/a/38459987/2309376) for a good description of why property injection is not done. – Simply Ged Oct 25 '18 at 06:48
  • @SimplyGed . You are right. Classes that support DI cannot be used as general classes and we cannot use much of the Design patterns. The problem with the above code is that it has coupling with the base class which makes it hard to maintain and scale. – Renil Babu Aug 17 '20 at 04:44