Наследование - производные классы, необходимые для создания экземпляра конструктора базового класса - PullRequest
0 голосов
/ 10 октября 2019

У меня нет понимания в этой основной концепции ООП. Вот пример:

Скажем, у меня есть базовый репозиторий, производный от 3 разных классов. Базовому классу требуется dbContext в конструкторе, внедренный путем внедрения зависимости. Из-за этого дочерние классы должны передавать dbContext каждый раз, когда я их создаю. Требование простое: базовый класс должен создавать экземпляр Context для дочерних классов. Это возможно? Я не хочу, чтобы дочерние классы беспокоились о контексте. Я просто хочу, чтобы они назвали его.

Вот мой базовый класс.

        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;
}

Вот как будет выглядеть дочерний класс:

         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>();
                }
            }

Вот мой контекстclass

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

А вот и файл startup.cs

  public class Startup
    {

        public void ConfigureServices(IServiceCollection services)
        {

            // DB Context

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

Я использую шаблон стратегии, чтобы определить, какой дочерний класс будет доступен во время выполнения. Для этого также необходимо, чтобы CastingContext внедрил конструктор, что опять-таки безобразно. Дело в том, что должен быть только один класс, предоставляющий контекст всем производным классам или, возможно, с помощью статического метода. Не могли бы вы помочь мне понять, как это сделать?

Вот мой класс стратегии:

  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();
    }
}

Ответы [ 3 ]

2 голосов
/ 10 октября 2019

Нет, вы не можете одновременно использовать внедрение зависимостей и , жестко закодировать создание вашей зависимости в базовом классе. Если вы хотите внедрить зависимость от внешнего потребителя, она должна пройти через производный конструктор.

В лучшем случае , вы можете передать локатор службы, который базовый конструктор затем использует для выборкиdbcontext, но затем вы все еще обрабатываете локатор службы в производном классе. Но сервисные локаторы больше не считаются хорошей практикой , потому что они имеют заметные недостатки, такие как снижение удобочитаемости / удобства использования и становятся общей неприятностью для обработки в достаточно больших кодовых базах.
Хотя это технически достигнет того, чего вы хотитедостигните, не начинайте использовать статические сервисные локаторы. Затраты далеко перевешивают выгоды.

Однако, проблема с самого начала не совсем понятна для меня. Если вы используете внедрение зависимостей, я бы предположил, что вы используете какое-то автоматическое внедрение, когда вам не нужно вручную создавать все зависимости.
Да, вам все равно нужно упомянуть dbcontext как (производный и базовый), но ваши производные классы не должны обрабатывать его (кроме передачи его базовому конструктору. Усилия для этого минимальны и обеспечивают максимальную свободу с точки зрения внедрения зависимостей.

Вот как наследование работает по замыслу.

1 голос
/ 11 октября 2019

Вы должны предоставить зависимость через дочерний класс, потому что, когда вы создаете экземпляр дочернего класса, базовый класс также создается, и, следовательно, должны быть предоставлены зависимости.

Конечно, дляпростоту кодирования и управления этими зависимостями можно использовать. Использовать контейнеры IoC, такие как Unity , для обработки автоматических инъекций зависимостей (контейнер внедрения зависимостей Unity Application с поддержкой внедрения конструктора, свойства и вызова метода)

1 голос
/ 10 октября 2019

Это функциональное ограничение C # как языка. У вас нет для предоставления того же конструктора (ов) в дочерних классах, что и у базового класса, но вы должны удовлетворять этим конструкторам в базовом классе. Например:

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

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

Или:

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

В любом случае, вы так или иначе должны каким-то образом предоставить экземпляр Dependency конструктору базового класса, но вы можете не делать этого. на самом деле создайте дочерний класс с этой зависимостью, и вместо этого получите его внутренне.

Однако, с практической точки зрения, с чем-то вроде DbContext, который полагается на вводимые зависимости, он потребует переноса этой зависимости всепуть: вашему дочернему классу понадобится конструктор, в который он может быть вставлен, чтобы затем перейти в конструктор базового класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...