SmartDto.NET
1.0.0
See the version list below for details.
dotnet add package SmartDto.NET --version 1.0.0
NuGet\Install-Package SmartDto.NET -Version 1.0.0
<PackageReference Include="SmartDto.NET" Version="1.0.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="SmartDto.NET" Version="1.0.0" />
<PackageReference Include="SmartDto.NET"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add SmartDto.NET --version 1.0.0
#r "nuget: SmartDto.NET, 1.0.0"
#:package SmartDto.NET@1.0.0
#addin nuget:?package=SmartDto.NET&version=1.0.0
#tool nuget:?package=SmartDto.NET&version=1.0.0
SmartDto.NET
A C# source generator that produces strongly-typed Create and Update request DTOs from your domain models. No reflection, no runtime overhead.
Install
dotnet add package SmartDto.NET
Quick start
Mark your model with [Dto]:
using SmartDto.NET;
[Dto]
public class Player
{
[DtoIgnore]
public int Id { get; set; }
public required string Name { get; set; }
public int Level { get; set; }
public string? Email { get; set; }
}
Register the JSON converter:
builder.Services.AddControllers()
.AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(
new PatchFieldJsonConverterFactory()));
Use the generated records in your controller:
[HttpPost]
public IActionResult Create([FromBody] CreatePlayerRequest request)
{
var errors = request.Validate();
if (errors.Count > 0)
return BadRequest(new { errors });
Player player = request.ToEntity();
return Ok(player);
}
[HttpPatch("{id}")]
public IActionResult Update(int id, [FromBody] UpdatePlayerRequest request)
{
var errors = request.Validate();
if (errors.Count > 0)
return BadRequest(new { errors });
request.ApplyTo(existingPlayer);
return Ok(existingPlayer);
}
What gets generated
For each [Dto] class the generator produces two immutable records:
public record CreatePlayerRequest
{
public PatchField<string> Name { get; init; } // required in Validate()
public PatchField<int> Level { get; init; } // optional
public PatchField<string?> Email { get; init; } // optional, nullable
public List<string> Validate() { ... }
public Player ToEntity() { ... }
}
public record UpdatePlayerRequest
{
public PatchField<string> Name { get; init; } // optional, but non-null if sent
public PatchField<int> Level { get; init; }
public PatchField<string?> Email { get; init; }
public List<string> Validate() { ... }
public void ApplyTo(Player target) { ... }
}
PatchField<T> distinguishes three states: not sent (HasValue = false), sent with value, and sent as null.
Attributes
| Attribute | Target | Description |
|---|---|---|
[Dto] |
Class | Enables generation for this model |
[DtoIgnore] |
Property | Excludes from generated DTOs |
[DtoName("json_name")] |
Property | Overrides the JSON property name |
[CreateOnly] |
Property | Only appears in the Create request |
[UpdateOnly] |
Property | Only appears in the Update request |
required keyword
Properties marked required are validated as mandatory in CreateRequest.Validate(). In UpdateRequest, all properties are optional.
Custom names
[Dto(CreateName = "NewPlayerDto", UpdateName = "EditPlayerDto")]
public class Player { ... }
Create/Update-only properties
[Dto]
public class Player
{
public required string Name { get; set; }
[CreateOnly]
public required string Password { get; set; } // only in CreatePlayerRequest
[UpdateOnly]
public string? DeactivationReason { get; set; } // only in UpdatePlayerRequest
}
JSON serializer support
The generator detects which serializers are available and produces the corresponding converters:
| Serializer | Generated converter | Registration |
|---|---|---|
| System.Text.Json | PatchFieldJsonConverterFactory |
options.Converters.Add(new PatchFieldJsonConverterFactory()) |
| Newtonsoft.Json | PatchFieldNewtonsoftConverter |
settings.Converters.Add(new PatchFieldNewtonsoftConverter()) |
Both are generated if both packages are referenced. No converter is auto-registered -- you choose which one to use.
Custom validation
Simple validator
Implement IDtoValidator<T> and point to it from the attribute:
public class MyCreateValidator : IDtoValidator<CreatePlayerRequest>
{
public List<string> Validate(CreatePlayerRequest instance)
{
var errors = new List<string>();
if (instance.Name.HasValue && instance.Name.Value.Length < 3)
errors.Add("Name must be at least 3 characters.");
return errors;
}
}
[Dto(CreateValidator = typeof(MyCreateValidator))]
public class Player { ... }
When a validator is set, Validate() delegates entirely to it -- the default generated rules are replaced.
FluentValidation
When FluentValidation is installed, the generator produces:
FluentDtoValidator<T>-- base class bridging FluentValidation withIDtoValidator<T>CreatePlayerRequestBaseValidator-- contains the generated required/null rulesUpdatePlayerRequestBaseValidator-- contains the generated null rules
Inherit to extend:
public class MyCreateValidator : CreatePlayerRequestBaseValidator
{
public MyCreateValidator()
{
RuleFor(x => x.Name)
.Must(f => !f.HasValue || f.Value.Length >= 3)
.WithMessage("Name must be at least 3 characters.");
}
}
[Dto(CreateValidator = typeof(MyCreateValidator))]
public class Player { ... }
Or start from scratch:
public class MyCreateValidator : FluentDtoValidator<CreatePlayerRequest>
{
public MyCreateValidator()
{
// your rules only
}
}
Requirements
- .NET 6+ (or .NET Framework with SDK-style projects and System.Text.Json NuGet)
requiredkeyword needs C# 11 / .NET 7+ (optional -- without it all properties are optional in Create)
License
MIT
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.