Shezi
Shezi

Reputation: 1342

Inheritance - derived classes required to instantiate the base class constructor

I have lack of understanding in this basic concept of OOP. Here is an example:

Say I have a base repository which is derived by 3 different classes. The base class requires the dbContext in the constructor , injected by dependency injection. Because of this the child classes have to pass the dbContext every time I instantiate them. The requirement is simple: The base class should instantiate the Context for the child classes. Is that possible? I do not want the child classes to worry about the context. I just want them to call it.

Here is my base class.

        public class CastingBaseRepository<TEntity> : ICastingBaseRepository<TEntity> where TEntity : class, IEntity
    { 
        private readonly CastingContext _context;
        public CastingBaseRepository(CastingContext context) => _context = context ?? throw new ArgumentNullException(nameof(context));

        // Context property
        public CastingContext Context => _context;
}

Heres how the child class would be:

         public class CommercialJobsRepository : CastingBaseRepository<Audition>, ICommercialJobsRepository
            { 
    /* I do not want to use base(context) here. I need a way for this class to just call Context property from the derived class.. Possible? */

                private CastingContext _context;
                public CommercialJobsRepository(CastingContext context) : base(context)
                {
                    _context = context;
                }

                public async Task<IList<OpenJobs>> GetOpenJobs()
                {
// test code
                    var tt = await _context.Audition.Take(10).ToListAsync();
                    return new List<OpenJobs>();
                }
            }

Here is my context class

    public partial class CastingContext : DbContext
{
     public virtual DbSet<Audition> Audition { get; set; }
    public CastingContext(DbContextOptions<CastingContext> options) : base(options)
    {
    }
}

And here is the startup.cs

  public class Startup
    {

        public void ConfigureServices(IServiceCollection services)
        {

            // DB Context

            services.AddDbContextPool<CastingContext>(options => { options.EnableSensitiveDataLogging(); options.UseSqlServer(Configuration.GetConnectionString("dbCast")); });
           }
}

I am using strategy pattern to determine which child class would be accessed at runtime. That too needs to have CastingContext injected the constructor which is again ugly. The point is, there should be only one class exposing the context to all derived classes or perhaps through a static method. Can you please help me understand how to do it?

Here is my Strategy class:

  public class JobsStrategyContext
{
    private readonly CastingContext _context;

    private readonly Dictionary<eBreakdownTypes, IJobsRepository> Strategies =
        new Dictionary<eBreakdownTypes, IJobsRepository>();

    public JobsStrategyContext(CastingContext context)
    {
        _context = context;

        Strategies.Add(eBreakdownTypes.Ftv, new FtvJobsRepository(_context));
        Strategies.Add(eBreakdownTypes.Commercial, new CommercialJobsRepository(_context));
        Strategies.Add(eBreakdownTypes.Theatre, new TheatreJobsRepository(_context));
    }


    public async Task<IList<OpenJobs>> GetOpenJobsBySubType(eBreakdownTypes breakdownType)
    {
        return await Strategies[breakdownType].GetOpenJobs();
    }
}

Upvotes: 0

Views: 3284

Answers (3)

Ashwini MN
Ashwini MN

Reputation: 11

You have to provide the dependency through the child class because, When you are creating an instance of the child class, the base class is also getting constructed and hence the dependencies must be supplied.

Of course for the ease of coding and managing these dependencies,you can use Use the IoC containers like Unity to handle the automatic dependency injections (The Unity Application dependency injection container with support for constructor, property, and method call injection)

Upvotes: 1

Chris Pratt
Chris Pratt

Reputation: 239380

This is a functional limitation of C# as a language. You don't have to provide the same constructor(s) in child classes as you have in the base class, but you must satisfy those constructors in the base class. For example:

public class BaseClass
{
    public BaseClass(Dependency dep) {}
}

public class ChildClass
{
    public ChildClass(Dependency dep) : base(dep) {}
}

Or:

public class ChildClass
{
    public ChildClass() : base(new Dependency()) {}
}

In either case, you somehow, someway must provide the Dependency instance to the base class constructor, but you may opt to not actually construct the child class with that dependency, and instead, get it internally.

However, in practical terms with something like DbContext which relies itself on dependencies being injected, it's going to require carrying that dependency all the way through: your child class will need a constructor where it can be injected in order to then pass down into the base class constructor.

Upvotes: 2

Flater
Flater

Reputation: 13803

No, you cannot both at the same time use dependency injection and hardcode the instantiation of your dependency in the base class. If you want to inject the dependency from an external consumer, it needs to pass through the derived constructor.

At best, you can pass a service locator which the base constructor then uses to fetch the dbcontext, but then you're still handling the service locator in the derived class. But service locators are no longer considered good practice because they have notable drawbacks such as decreasing readability/usability and becoming a general nuisance to handle in sufficiently large codebases.
While it would technically achieve what you want to achieve, don't start using static service locators. The costs far outweigh the benefits.

However, the problem outset doesn't quite make sense to me. If you're using dependency injection, I would assume you're using some sort of automated injection where you don't have to manually instantiate all dependencies.
Yes, you still need to mention the dbcontext as a (derived and base) constructor parameter, but your derived classes don't need to handle it (other than passing it to the base constructor. The effort to do so is minimal and it allows for maximum freedom in terms of dependency injection.

This is how inheritance works by design.

Upvotes: 2

Related Questions