Pekspro.DataAnnotationValuesExtractor by Pekspro
NuGet / site data
Details
Info
Name: Pekspro.DataAnnotationValuesExtractor
A source generator for creating constants from data annotations.
Author: Pekspro
NuGet: https://www.nuget.org/packages/Pekspro.DataAnnotationValuesExtractor/
You can find more details at https://github.com/pekspro/DataAnnotationValuesExtractor
Source: https://github.com/pekspro/DataAnnotationValuesExtractor
Author
Pekspro

Original Readme
DataAnnotationValuesExtractor
A C# source generator that automatically extracts values from data annotation
attributes and exposes them as strongly-typed constants. Access your
StringLength, Range, Required and Display attribute values as constants
in your classes.
Why Use This?
When working with data annotations, you often need to reference validation constraints. A good way to solve it is to create constants. But it takes time to do and makes your data models harder to read. And it's harder when your models are auto generated like when you are scaffolding with Entity Framework.
This source generator creates the constants automatically for you. If you have this model:
public partial class Product
{
[Required]
[StringLength(100)]
public string? Name \{ get; set; }
[Required]
[Range(0.01, 999999.99)]
public decimal Price \{ get; set; }
}
Constants will be generated that you can access like this:
// Name length constraints
int maxNameLength = Product.Annotations.Name.MaximumLength; // 100
bool nameRequired = Product.Annotations.Name.IsRequired; // true
// Price constraints
double minPrice = Product.Annotations.Price.Minimum; // 0.01
double maxPrice = Product.Annotations.Price.Maximum; // 999999.99
Usage Patterns
There are two ways to configure DataAnnotationValuesExtractor depending on your needs:
######### 1. Direct Approach
Apply [DataAnnotationValues] directly to each class you want to generate
constants for:
[DataAnnotationValues(StringLength = true, Range = true, Required = true, Display = true)]
public partial class Product
{
[Display(Name = "Product name")]
[Required]
[StringLength(100)]
public string? Name \{ get; set; }
[Display(Name = "Product price")]
[Required]
[Range(0.01, 999999.99)]
public decimal Price \{ get; set; }
public string? Sku \{ get; set; }
}
######### 2. Centralized Approach
Create a dummy class and use the DataAnnotationValuesToGenerate attribute for
each class you want to generate constants for. You can use the
DataAnnotationValuesConfiguration attribute to configure what to be generated.
using Pekspro.DataAnnotationValuesExtractor;
[DataAnnotationValuesConfiguration(StringLength = true, Range = true, Required = true, Display = true)]
[DataAnnotationValuesToGenerate(typeof(Customer))]
[DataAnnotationValuesToGenerate(typeof(Order))]
[DataAnnotationValuesToGenerate(typeof(Product))]
partial class ValidationConstants
{
}
Your model classes remain unchanged.
This approach is especially useful when working with auto-generated models, such as those created by Entity Framework scaffolding. If you do, and you have all your models in a folder, you can use this PowerShell script to generate the attributes for all models in that folder:
Get-ChildItem -Filter '*.cs' |
Where-Object \{ -not ($_.BaseName -match '(?i)context') \} |
ForEach-Object \{ "[DataAnnotationValuesToGenerate(typeof($($_.BaseName)))]" \} |
Set-Clipboard
######### Use the generated constants
No matter which approach your are using, you can access generated constants like this:
// Name
int maxNameLength = Product.Annotations.Name.MaximumLength; // 100
int minNameLength = Product.Annotations.Name.MinimumLength; // 0
bool nameRequired = Product.Annotations.Name.IsRequired; // true
string? nameDisplayName = Product.Annotations.Name.Display.Name; // Product name
// Price
double minPrice = Product.Annotations.Price.Minimum; // 0.01
double maxPrice = Product.Annotations.Price.Maximum; // 999999.99
bool priceRequired = Product.Annotations.Price.IsRequired;
string? priceDisplayName = Product.Annotations.Price.Display.Name; // Price name
// Sku
bool skuRequired = Product.Annotations.Sku.IsRequired; // false
Installation
Add the package to your project:
dotnet add package Pekspro.DataAnnotationValuesExtractor
For optimal setup, configure the package reference in your .csproj to exclude
it from your output assembly:
<ItemGroup>
<PackageReference Include="Pekspro.DataAnnotationValuesExtractor" Version="0.0.1"
PrivateAssets="all" ExcludeAssets="runtime" />
</ItemGroup>
Why these attributes?
PrivateAssets="all"- Projects referencing yours won't inherit this package, they don't need it.ExcludeAssets="runtime"- The attributes DLL won't be copied to your build output, this is also not needed.
Configuration Options
Both [DataAnnotationValues] and [DataAnnotationValuesConfiguration] support
the following properties to control which constants are generated:
| Property | Default | Generated Constants | Description |
|---|---|---|---|
StringLength | true | MaximumLength, MinimumLength | Extract values from [StringLength] attribute. |
Range | true | Minimum, Maximum, MinimumIsExclusive, MaximumIsExclusive | Extract values from [Range] attribute. |
Required | false | IsRequired | Detect presence of [Required] attribute. |
Display | false | Name, Description, ShortName | Extract values from [Display] attribute. |
Do you miss some annotations? Create an issue and let me know.
Viewing Generated Code
There are two ways to inspect the generated source code:
######### Method 1: Go to Definition
Right-click on your class name and select Go to Definition (F12). Visual Studio will show you the generated partial class.
######### Method 2: Output to File
Add this to your .csproj file to save generated files to disk:
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\$(Configuration)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
Generated files will be saved to obj\[Configuration]\GeneratedFiles\
directory.
Generated Code Example
Given this input:
[DataAnnotationValues(StringLength = true, Range = true, Required = true, Display = true)]
public partial class Player
{
[Display(Name = "Player name", ShortName ="Name", Description = "Name of player")]
[Required]
[StringLength(50)]
public string? Name \{ get; set; }
[StringLength(100, MinimumLength = 6)]
public string? Email \{ get; set; }
[Range(1, 100)]
public int Score \{ get; set; }
}
The generator produces:
public partial class Player
{
/// <summary>
/// Data annotation values.
/// </summary>
public static class Annotations
{
/// <summary>
/// Data annotation values for Name.
/// </summary>
public static class Name
{
/// <summary>
/// Maximum length for Name.
/// </summary>
public const int MaximumLength = 50;
/// <summary>
/// Minimum length for Name.
/// </summary>
public const int MinimumLength = 0;
/// <summary>
/// Indicates whether Name is required.
/// </summary>
public const bool IsRequired = true;
/// <summary>
/// Display attribute values for Name.
/// </summary>
public static class Display
{
/// <summary>
/// Display name for Name.
/// </summary>
public const string? Name = "Player name";
/// <summary>
/// Short display name for Name.
/// </summary>
public const string? ShortName = "Name";
/// <summary>
/// Description for Name.
/// </summary>
public const string? Description = "Name of player";
}
}
/// <summary>
/// Data annotation values for Email.
/// </summary>
public static class Email
{
/// <summary>
/// Maximum length for Email.
/// </summary>
public const int MaximumLength = 100;
/// <summary>
/// Minimum length for Email.
/// </summary>
public const int MinimumLength = 6;
/// <summary>
/// Indicates whether Email is required.
/// </summary>
public const bool IsRequired = false;
}
/// <summary>
/// Data annotation values for Score.
/// </summary>
public static class Score
{
/// <summary>
/// Minimum value for Score.
/// </summary>
public const int Minimum = 1;
/// <summary>
/// Maximum value for Score.
/// </summary>
public const int Maximum = 100;
/// <summary>
/// Indicates whether the minimum value for Score is exclusive.
/// </summary>
public const bool MinimumIsExclusive = false;
/// <summary>
/// Indicates whether the maximum value for Score is exclusive.
/// </summary>
public const bool MaximumIsExclusive = false;
/// <summary>
/// Indicates whether Score is required.
/// </summary>
public const bool IsRequired = false;
}
}
}
Requirements
- .NET SDK 7.0 or later - Required for the source generator to run
- Target Framework - Can target .NET Core 3.1, .NET Standard 2.0, or any later framework
- Partial Classes - Generated code uses partial classes, so it must be in the same assembly as your models
Note: You need .NET 7 SDK installed, but your project can target earlier frameworks.
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests on GitHub.
Credits
This project is heavily inspired by the NetEscapades.EnumExtractors project.
License
This project is licensed under the MIT License. See the LICENSE file for details.
About
Generating code to extract values from data annotations in C#.
How to use
Example (source csproj, source files)
- CSharp Project
- Program.cs
- Person.cs
- Values.cs
This is the CSharp Project that references Pekspro.DataAnnotationValuesExtractor
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Pekspro.DataAnnotationValuesExtractor" Version="1.0.0" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
This is the use of Pekspro.DataAnnotationValuesExtractor in Program.cs
// See https://aka.ms/new-console-template for more information
using Attr;
Console.WriteLine(Person.Annotations.Age.Minimum);
Console.WriteLine(Person.Annotations.FirstName.MinimumLength);
Console.WriteLine(Person.Annotations.FirstName.MaximumLength);
This is the use of Pekspro.DataAnnotationValuesExtractor in Person.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Attr;
partial class Person
{
[Display(Name = "First name")]
[Required]
[StringLength(100,MinimumLength =3)]
public string? FirstName \{ get; set; }
[Required]
[Range(18, 200)]
public int Age \{ get; set; }
}
This is the use of Pekspro.DataAnnotationValuesExtractor in Values.cs
using Pekspro.DataAnnotationValuesExtractor;
namespace Attr;
[DataAnnotationValuesOptions(StringLength = true, Range = true, Required = true, Display = true)]
[DataAnnotationValuesToGenerate(typeof(Person))]
partial class Values
{
}
Generated Files
Those are taken from $(BaseIntermediateOutputPath)\GX
- Values.g.cs
//---------------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the Pekspro.DataAnnotationValuesExtractor source generator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//---------------------------------------------------------------------------------------
#nullable enable
namespace Attr
{
/// <summary>
/// Data annotation values for Person.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Pekspro.DataAnnotationValuesExtractor", "1.0.0")]
public partial class Person
{
/// <summary>
/// Data annotation values.
/// </summary>
public static class Annotations
{
/// <summary>
/// Data annotation values for FirstName.
/// </summary>
public static class FirstName
{
/// <summary>
/// Maximum length for FirstName.
/// </summary>
public const int MaximumLength = 100;
/// <summary>
/// Minimum length for FirstName.
/// </summary>
public const int MinimumLength = 3;
/// <summary>
/// Indicates whether FirstName is required.
/// </summary>
public const bool IsRequired = true;
/// <summary>
/// Display attribute values for FirstName.
/// </summary>
public static class Display
{
/// <summary>
/// Display name for FirstName.
/// </summary>
public const string? Name = "First name";
/// <summary>
/// Short display name for FirstName.
/// </summary>
public const string? ShortName = null;
/// <summary>
/// Description for FirstName.
/// </summary>
public const string? Description = null;
}
}
/// <summary>
/// Data annotation values for Age.
/// </summary>
public static class Age
{
/// <summary>
/// Minimum value for Age.
/// </summary>
public const int Minimum = 18;
/// <summary>
/// Maximum value for Age.
/// </summary>
public const int Maximum = 200;
/// <summary>
/// Indicates whether the minimum value for Age is exclusive.
/// </summary>
public const bool MinimumIsExclusive = false;
/// <summary>
/// Indicates whether the maximum value for Age is exclusive.
/// </summary>
public const bool MaximumIsExclusive = false;
/// <summary>
/// Indicates whether Age is required.
/// </summary>
public const bool IsRequired = true;
}
}
}
}
Useful
Download Example (.NET C#)
Share Pekspro.DataAnnotationValuesExtractor
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Pekspro.DataAnnotationValuesExtractor
Category "EnhancementClass" has the following generators:
1 ApparatusAOT
2023-04-16
2 AspectGenerator
2024-01-07
3 CommonCodeGenerator
2024-04-03
4 Comparison
2025-05-25
5 DudNet
2023-10-27
6 Enhanced.GetTypes
2024-09-17
7 FastGenericNew
2023-08-10
8 Immutype
2023-08-12
9 Ling.Audit
2023-12-12
10 Lombok.NET
2023-04-16
11 M31.FluentAPI
2023-08-25
12 MemberAccessor
2025-03-24
13 MemoryPack
2023-08-04
14 Meziantou.Polyfill
2023-10-10
15 Microsoft.Extensions.Logging
2023-04-16
16 Microsoft.Extensions.Options.Generators.OptionsValidatorGenerator
2023-11-17
17 Microsoft.Interop.JavaScript.JSImportGenerator 2023-04-16
18 OptionToStringGenerator
2024-02-15
19 Pekspro.DataAnnotationValuesExtractor
2026-02-15
20 Program
2025-11-06
21 QueryStringGenerator
2024-11-07
22 RSCG_Decorator
2023-09-30
23 RSCG_UtilityTypes
2023-12-22
24 StaticReflection
2023-10-13
25 SyncMethodGenerator
2023-08-14
26 System.Runtime.InteropServices
2023-04-16
27 System.Text.RegularExpressions
2023-04-16
28 TelemetryLogging
2023-11-30
29 ThisClass
2024-04-19