skip to Main Content

Is it possible to have interfaces with other paramaters together like here below?
I do this because I have private setters and have one constructor.

planning constructor (Logic)

public Planning(
    IPlanningDAL planningDAL,
    IPlanningParticipantDAL planningParticipantDAL,
    ICategoryCollectionDAL categoryCollectionDAL,
    ITaskCollectionDAL taskCollectionDAL,
    ITaskDAL taskDAL,
    IParticipantDAL participantDAL,
    ICategoryDAL categoryDAL,
    int id,
    string name,
    DateTime startDate,
    DateTime? endDate,
    Participant leader)
{
    this.planningDAL = planningDAL;
    this.planningParticipantDAL = planningParticipantDAL;
    this.categoryCollectionDAL = categoryCollectionDAL;
    this.taskCollectionDAL = taskCollectionDAL;
    this.taskDAL = taskDAL;
    this.participantDAL = participantDAL;
    this.categoryDAL = categoryDAL;
    Id = id;
    Name = name;
    StartDate = startDate;
    EndDate = endDate;
    Leader = leader;
}

ASP.NET Core MVC program

builder.Services.AddScoped<Planning>();

error

System.AggregateException: ‘Some services are not able to be constructed (Error while validating the service descriptor ‘ServiceType: Logic.Models.Planning Lifetime: Scoped ImplementationType: Logic.Models.Planning’: Unable to resolve service for type ‘System.Int32’ while attempting to activate ‘Logic.Models.Planning’.)

2

Answers


  1. From the context of the question I distilled the following:

    • Planning represents an entity and considering the number of design-time dependencies (such as your IPlanningDAL) the class has, it seems you are trying to apply some sort of Domain-Driven Design. An entity is an object that is primarily defined by its identity (e.g. the int Id in your case).
    • The more-primitive properties int Id, string Name, DateTime StartDate, DateTime? EndDate, and Participant Leader` seem values define the entity’s current state, rather than configuration values that stay unchanged for all entities in the system and while the system is running.
    • By supplying the entity’s data values through the constructor you seem to be trying to protect the class’s invariant. This is a common approach when practicing Domain-Driven Design where you prevent direct mutation of individual properties from the outside, but rather let consumers change the state of an entity through methods on the entity.

    The remainder of my answer rest on the previous assumptions.

    Object Composition, in the context of Dependency Injection, is meant to compose application components like a behavior-centric SqlPlanningDAL. It’s not meant for the construction of data-centric object like entities. The use of DI Containers exaggerate this, because it becomes hard to create data-centric objects with design-time dependencies when using a DI Container such as the built-in ASP.NET Core DI Container Microsoft.Extensions.DependencyInjection (MS.DI). This means that adding Planning to the Container such as you are doing here is not a good idea:

    builder.Services.AddScoped<Planning>();
    

    The reason for this is that although the container can supply the configured design-time dependencies to the class, it has no way of knowing what runtime values it should provide. And how could it? These values will change for each entity and are typically loaded from some data source.

    This means that any time the construction of an object requires both runtime data and design-time dependencies (as with your Planning entity), object composition gets complicated, which is why I consider injecting runtime data into components a code DI smell.

    A way to solve this is by moving the creation of Planning to a repository of some sort (likely your PlanningDAL):

    // WARNING: Not an ideal solution
    public class PlanningDal : IPlanningDal
    {
        public PlanningDal(
            IPlanningDAL planningDAL,
            IPlanningParticipantDAL planningParticipantDAL,
            ICategoryCollectionDAL categoryCollectionDAL,
            ITaskCollectionDAL taskCollectionDAL,
            ITaskDAL taskDAL,
            IParticipantDAL participantDAL,
            ICategoryDAL categoryDAL) ...
    
        public Planning GetById(int id)
        {
            PlanningData data = LoadPlanningDataFromDatabase(id);
    
            return new Planning(
                this.planningDAL,
                this.planningParticipantDAL,
                this.categoryCollectionDAL,
                this.taskCollectionDAL,
                this.taskDAL,
                this.participantDAL,
                this.categoryDAL,
                data.Id,
                data.Name,
                data.StartDate,
                data.EndDate,
                data.Participant);
        }
    }
    

    Although this could certainly technically work, this model will —in the long run— likely become a maintenance nightmare. This is because in an evolving domain, new domain logic will be added regularly, which will force changes to both the PlanningDal (i.e. your repository) and the Planning entity.

    A more commonly-used —and more-maintainable— model is to move Planning‘s design-time dependencies to specific methods on Planning that require their use. So instead of applying them using Constructor Injection, you use Method Injection instead. This means that only the entity’s data values will be applied to the constructor:

    public class Planning : IEntity
    {
        // Make setters private to protect invariants.
        public int Id { get; private set; }
        public string Name { get; private set; }
        public DateTime StartDate { get; private set; }
        public DateTime? EndDate { get; private set; }
        public Participant Participant { get; private set; }
    
        // Only supply runtime data here
        public Planning(
            int id, string name, DateTime startDate, DateTime? endDate, Participant leader)
        {
            Id = id;
            Name = name;
            StartDate = startDate;
            EndDate = endDate;
            Leader = leader;
        }
        
        // Here you implement the logic where you pass on -only- the design-time dependencies
        // that are required for the method to function. The method should not store the
        // dependencies; only use them.
        public void ReplaceParticipant(
            Participant participant, IPlanningParticipantDAL planningParticipantDAL)
        {
            ...
            this.Participant = participant;
        }    
    }
    

    This does mean, however, that those dependencies must be supplied by consumers of these domain methods. For instance:

    public class ReplaceParticipantHandler : ICommandHandler<ReplaceParticipant>
    {
        private readonly IPlanningDAL planningDAL;
        private readonly IParticipantDAL participantDal;
        private readonly IPlanningParticipantDAL planningParticipantDal;
    
        public ReplaceParticipantHandler(
            IPlanningDAL planningDAL,
            IParticipantDAL participantDal,
            IPlanningParticipantDAL planningParticipantDal)
        {
            this.planningDAL = planningDAL;
            this.participantDal = participantDal;
            this.planningParticipantDal = planningParticipantDal;
        }
        
        public void Handle(ReplaceParticipant command)
        {
            // Load entities using repositories
            var planning = this.planningDAL.GetById(command.PlanningId);
            var participant = this.planningParticipantDAL.GetById(command.NewParticipantId);
            
            // Invoke domain method
            planning.ReplaceParticipant(participant, this.planningParticipantDal);
            
            // use repository to persist the entity
            this.planningDAL.Save(planning);
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search