diff --git a/Directory.Build.props b/Directory.Build.props
index 30a5d8c..8a6d14d 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -71,4 +71,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/OptionalValues.slnx b/OptionalValues.slnx
index e4203b9..e44e9eb 100644
--- a/OptionalValues.slnx
+++ b/OptionalValues.slnx
@@ -18,6 +18,7 @@
+
@@ -29,6 +30,7 @@
Path="test/OptionalValues.DataAnnotations.Tests/OptionalValues.DataAnnotations.Tests.csproj" />
+
diff --git a/README.md b/README.md
index 7796293..f707a77 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ A .NET library that provides an `OptionalValue` type, representing a value th
| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| [OptionalValues](https://www.nuget.org/packages/OptionalValues) | [](https://www.nuget.org/packages/OptionalValues) |
| [OptionalValues.OpenApi](https://www.nuget.org/packages/OptionalValues.OpenApi) | [](https://www.nuget.org/packages/OptionalValues.OpenApi) |
+| [OptionalValues.Mvc](https://www.nuget.org/packages/OptionalValues.Mvc) | [](https://www.nuget.org/packages/OptionalValues.Mvc) |
| [OptionalValues.Swashbuckle](https://www.nuget.org/packages/OptionalValues.Swashbuckle) | [](https://www.nuget.org/packages/OptionalValues.Swashbuckle) |
| [OptionalValues.NSwag](https://www.nuget.org/packages/OptionalValues.NSwag) | [](https://www.nuget.org/packages/OptionalValues.NSwag) |
| [OptionalValues.DataAnnotations](https://www.nuget.org/packages/OptionalValues.DataAnnotations) | [](https://www.nuget.org/packages/OptionalValues.DataAnnotations) |
@@ -85,6 +86,7 @@ Optionally, install one or more extension packages:
```bash
dotnet add package OptionalValues.Swashbuckle
dotnet add package OptionalValues.NSwag
+dotnet add package OptionalValues.Mvc
dotnet add package OptionalValues.DataAnnotations
dotnet add package OptionalValues.FluentValidation
```
@@ -352,7 +354,7 @@ public class Model
The `OptionalValues` library integrates seamlessly with ASP.NET Core, allowing you to use `OptionalValue` properties in your API models.
-You only need to configure the `JsonSerializerOptions` to include the `OptionalValue` converter:
+Configure the `JsonSerializerOptions` to include the `OptionalValue` converter, and for MVC controller validation add `OptionalValues.Mvc`:
```csharp
// For Minimal API
@@ -364,6 +366,23 @@ builder.Services.ConfigureHttpJsonOptions(jsonOptions =>
// For MVC
builder.Services.AddControllers()
+ .AddJsonOptions(options =>
+ {
+ options.JsonSerializerOptions.AddOptionalValueSupport();
+ })
+ .AddMvcOptions(options =>
+ {
+ options.AddOptionalValueSupport();
+ });
+```
+
+Or configure the MVC options directly:
+
+```csharp
+builder.Services.AddControllers(options =>
+ {
+ options.AddOptionalValueSupport();
+ })
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.AddOptionalValueSupport();
diff --git a/src/OptionalValues.Mvc/MvcOptionsExtensions.cs b/src/OptionalValues.Mvc/MvcOptionsExtensions.cs
new file mode 100644
index 0000000..e00b459
--- /dev/null
+++ b/src/OptionalValues.Mvc/MvcOptionsExtensions.cs
@@ -0,0 +1,25 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace OptionalValues.Mvc;
+
+///
+/// Extension methods for to add support for validation metadata.
+///
+public static class MvcOptionsExtensions
+{
+ ///
+ /// Adds validation metadata support for .
+ ///
+ /// The MVC options to configure.
+ public static void AddOptionalValueSupport(this MvcOptions options)
+ {
+ ArgumentNullException.ThrowIfNull(options);
+
+ if (options.ModelMetadataDetailsProviders.OfType().Any())
+ {
+ return;
+ }
+
+ options.ModelMetadataDetailsProviders.Add(new OptionalValueValidationMetadataProvider());
+ }
+}
diff --git a/src/OptionalValues.Mvc/NeverValidatePropertyFilter.cs b/src/OptionalValues.Mvc/NeverValidatePropertyFilter.cs
new file mode 100644
index 0000000..8607209
--- /dev/null
+++ b/src/OptionalValues.Mvc/NeverValidatePropertyFilter.cs
@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
+
+namespace OptionalValues.Mvc;
+
+internal sealed class NeverValidatePropertyFilter : IPropertyValidationFilter
+{
+ internal static NeverValidatePropertyFilter Instance { get; } = new();
+
+ public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry) => false;
+}
diff --git a/src/OptionalValues.Mvc/OptionalValueValidationMetadataProvider.cs b/src/OptionalValues.Mvc/OptionalValueValidationMetadataProvider.cs
new file mode 100644
index 0000000..5632531
--- /dev/null
+++ b/src/OptionalValues.Mvc/OptionalValueValidationMetadataProvider.cs
@@ -0,0 +1,32 @@
+using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
+
+namespace OptionalValues.Mvc;
+
+///
+/// Provides MVC validation metadata for .
+///
+internal sealed class OptionalValueValidationMetadataProvider : IValidationMetadataProvider
+{
+ ///
+ public void CreateValidationMetadata(ValidationMetadataProviderContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+
+ if (context.Key.MetadataKind != ModelMetadataKind.Property ||
+ context.Key.ContainerType is null ||
+ !OptionalValue.IsOptionalValueType(context.Key.ContainerType))
+ {
+ return;
+ }
+
+ switch (context.Key.Name)
+ {
+ case nameof(OptionalValue