C
C#ā€¢2y ago
D.Mentia

ā” How to structure DTOs

If you're making a DTO, do you make it like this
public class UpdateStudentDTO
{
public int Id;
public string? Name;
public string? Address; // etc
}
public class UpdateStudentDTO
{
public int Id;
public string? Name;
public string? Address; // etc
}
Or like this
public class StudentDTO{
OperationTypes Type; // OperationTypes.Update, .Create, .Delete
public int? Id;
public string? Name; // etc
}
public class StudentDTO{
OperationTypes Type; // OperationTypes.Update, .Create, .Delete
public int? Id;
public string? Name; // etc
}
Or like this
public class UpdateStudentDTO{
public int Id;
public Student FieldsToUpdate;
}
public class UpdateStudentDTO{
public int Id;
public Student FieldsToUpdate;
}
Basically, should there be one DTO that is used for all OperationTypes, or one endpoint and DTO per type (Update/Delete/Etc)? And the last one is quite bad, I'm sure, but if the Student is complex and long, would it ever be acceptable?
15 Replies
Angius
Angiusā€¢2y ago
None of those, I never use public fields But assuming those were properties, each DTO should only ever hold exactly as much data as it needs And, ideally, each should be used only once So you don't reuse the same DTO for editing users and returning blogpost author
D.Mentia
D.MentiaOPā€¢2y ago
idk why adding 'public' is just reflex but the {get; set;} I thought I'd skip for brevity šŸ˜› So then followup question.... If I have some process that specifically update's a student's name, and another one that updates their address. Would you use two separate endpoints, two separate DTOs?
Angius
Angiusā€¢2y ago
Yep
D.Mentia
D.MentiaOPā€¢2y ago
(honestly at that scale I think you'd skip the DTO and just take in UpdateStudentName(int id, string name), though?)
Angius
Angiusā€¢2y ago
If you can bind it properly, sure, that works Personally, I just create a simple record next to the handler method and use that
[HttpPatch]
public Task<...> UpdateStudentName(StudentNameDto student)
{
// ...
}

public record StudentNameDto(string Name);
[HttpPatch]
public Task<...> UpdateStudentName(StudentNameDto student)
{
// ...
}

public record StudentNameDto(string Name);
D.Mentia
D.MentiaOPā€¢2y ago
That seems like a lot of duplicated effort, though, two separate endpoints to update two separate values Like they're both going to start by retrieving the student via ID. Which isn't a lot of duplicated code, but it's not none
Angius
Angiusā€¢2y ago
Well, to be perfectly honest, I use MediatR in the first place, so that issue solves itself lol Use just a single method, then
D.Mentia
D.MentiaOPā€¢2y ago
What do you mean?
Angius
Angiusā€¢2y ago
[HttpPatch]
public Task<...> UpdateStudent(UpdateStudentDto student)
{
var (name, birthday, ...) = student;

var student = await _ctx.Students.FindAsync(id);

if (name is {}) student.Name = name;
if (birthday is {}) student.Birthday = birthday;
if (... is {}) student.... = ...;

await _context.SaveCahngesAsync();
}

public record UpdateStudentDto(string? Name, DateTime? Birthday, ...);
[HttpPatch]
public Task<...> UpdateStudent(UpdateStudentDto student)
{
var (name, birthday, ...) = student;

var student = await _ctx.Students.FindAsync(id);

if (name is {}) student.Name = name;
if (birthday is {}) student.Birthday = birthday;
if (... is {}) student.... = ...;

await _context.SaveCahngesAsync();
}

public record UpdateStudentDto(string? Name, DateTime? Birthday, ...);
One update endpoint
D.Mentia
D.MentiaOPā€¢2y ago
But you said you'd have two DTOs šŸ˜› what you have there, makes sense to me
Angius
Angiusā€¢2y ago
I mean, you can do either You said initially you have two handlers, one for name, one for address
D.Mentia
D.MentiaOPā€¢2y ago
it's just awkward trying to find that line because both ways are annoying in different ways
Angius
Angiusā€¢2y ago
You'd have two DTOs in this case If you have one update endpoint, you'd have one DTO
D.Mentia
D.MentiaOPā€¢2y ago
either way, better than the way we do it now, which is basically
public class UpdateStudentOperation{
// NOTE: Only accepts updates to Name or Address
public Student Student {get; set;}
}
public class UpdateStudentOperation{
// NOTE: Only accepts updates to Name or Address
public Student Student {get; set;}
}
šŸ¤¢
Accord
Accordā€¢2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server