1
votes

I am in a situation where the creation of an entity requires the creation and ID of other entities, however, I am unsure how this is best implemented using MediatR.

For example, say I have a User object...

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
    public ICollection<Submission> Submissions { get; set; }
}

a Submission object...

public class Submission
{
    public int Id { get; set; }
    public string Reference { get; set; }
    public User User { get; set; }
    public ICollection<QuestionsAndAnswer> QuestionAndAnswers { get; set; }
}

and a QuestionAndAnswer object.

public class QuestionAndAnswer
{
    public int ID { get; set; }
    public string Question { get; set; }
    public string Answer { get; set; }
    public Submission { get; set; }
}

Each user can have multiple submissions and each submission has multiple questions and answers.

In this instance, the user has gone through a series of questions, provided answers, filled in the final form which provides asks for their contact details and then submitted the information to the API. At this point, I am unsure how to handle the data using MediatR, because the User must be created, with their ID returned as a result to create the submission object which in turn is required so that the questions and answers can be saved.

Should I implement a command which is called CreateUserWithSubmissionQuestionAndAnswersCommand, the handler of which somehow then calls the handlers for the three separate commands and executes them in order, and if so how?

I am just a bit lost on how to implement this scenario as a single HTTPRequest without violating CQRS. The only other way I could think of is implementing separate endpoints which perform each of these tasks individually which requires the client to make and receive three separate calls in succession (CreateUser, CreateSubmission, CreateQuestionAndAnswer)?

Anybody got any ideas?

1
I too am facing this issue, I am currently chaining handler calls in my controller. - Anton Toshik
@AntonToshik I agree, this seems to be the best solution for now. - Rob Jack Stewart
@RobJackStewart Did you ever find a "better" solution to this? - Adam Stapleton
@AdamStapleton It all comes down to the use case, as I have used both the marked answer and my original proposal of calling handlers from other handlers. I prefer the latter option but, keep a strict rule that you should only ever go two levels deep. e.g. you can have one handler call another, but you cannot have that second hadnelr call third as this means that your handlers are not atmoic. - Rob Jack Stewart

1 Answers

1
votes

I usually model the user interactions/user stories as MediatR requests. In most cases this means mapping an single HTTP request to a single MediatR request.

In your case this would be a single request which encapsulates the processing of the submission(s), e.g. ProcessSubmissions. This request would contain properties for the user as well as the submission(s) and the associated question/answer pairs. Then a single handler would persist the individual entities in the correct order.

For completeness, this is what this solution could look like:

public class ProcessSubmission : IRequest
{
    public User User { get; set; }

    public ICollection<Submission> Submissions { get; set; }
}

public class ProcessSubmissionHandler : IRequestHandler<ProcessSubmission>
{
    public async Task<Unit> Handle(ProcessSubmission request, CancellationToken cancellationToken)
    {
        // Persist the user in the database
        var userId = PersistTheUser(request.User);

        foreach (var submission in request.Submissions)
        {
            // Persist each submission with questions and answers
        }
    }
}