Reputation: 109
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'.)
Upvotes: 1
Views: 1280
Reputation: 172616
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).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.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);
}
}
Upvotes: 0
Reputation: 362
A question similar to your question asked before. I think the following link can help you .
Upvotes: 0