If I understand you right you have the following classes:
Question MultipleChoiceQuestion : Question Essay : Question .... : Question
And you want a single Create
endpoint:
Post Create(Question q)
Which will validate and save to a database any of the derived Question types.
First off, .NET won't magically know which derived type to deserialise the incoming JSON to. You'll need to add some cleverness type discriminators.
This needs some $type
field in the JSON, so:
Question { public string Type => this.getType().ToString(); // I'm sure you can do better. }
Now that you have the actual type in the controller you just need to select the validation logic. Given my previous answer where I stress the benefits of keeping the validation separate from the object, we want to avoid question.Validate()
.
QuestionController { [HttpPost] public Task Create(Question q) { IValidator v; if(!this.ValidationDictionary.TryGetValue(q.Type + "_Create", out v)) { throw new CantFindValidatorException(q.Type); } v.Validate(q); // throw validation exception with collection of error messages. handle in global exception handler which just converts to json and adds error code of your choice. this.repo.Add(q); //more looking at q.Type to decide which tables to put it in. } }
Given the fiddlyness of this and the need to check the type in multiple places and ensure its sent in the JSON, You have to consider the alternative of just adding extra controller actions:
QuestionController { [HttpPost] public Task CreateEssay(Essay q) { this.create(q); } [HttpPost] public Task CreateMCQ(MultipleChoiceQuestion q) { this.create(q); } private Task create(Question q) { IValidator v; if(!this.ValidationDictionary.TryGetValue(q.GetType(), out v)) { throw new CantFindValidatorException(q.GetType() + "_Create"); } v.Validate(q); select q.GetType() //or factory pattern of choice case "Essay" this.repo.AddEssay(q); ... } }
This approach levers the built in serialisation and route mapping.