Inspecting String Length Constraints in EF Core
Entity Framework Core exposes a rich representation of your database schema through its model metadata. This metadata reflects not only attributes on your entities, but also Fluent API configuration, conventions, and provider‑specific decisions.
In this post, we’ll look at a practical way to extract string length constraints from the EF Core model using:
- C#
- EF Core 8
- LINQPad 8
The goal is to surface, programmatically:
- 🧩 Entity name
- 🧱 Property name
- 🏷 Database column name
- 📏 Configured maximum string length (if any)
All of this is done without reflection and without querying database system tables.
Why Use EF Core Metadata?
It’s tempting to reach for reflection and scan attributes like [MaxLength] or
[StringLength]. However, reflection only tells you what was declared —
not what EF Core ultimately built.
EF Core metadata reflects the resolved model, which may include:
- ✅ Fluent API overrides
- ✅ Convention‑based lengths
- ✅ Provider defaults
- ✅ Shadow properties
- ✅ Mappings that never existed as CLR attributes
If EF Core generated it, the metadata knows about it.
Working in LINQPad 8
When you select an EF Core connection in LINQPad, LINQPad instantiates the DbContext for you.
Within the query, that context instance is available via this.
This makes LINQPad an excellent environment for exploratory tooling and schema inspection.
Entry point
void Main()
{
var dbContext = (DbContext)this;
Console.WriteLine("----------------- GET ALL THE ENTITY TYPE FIELDS/COLUMNS WITH A STRING LENGTH CONSTRAINT------------");
var stringlengthConstrainedField = dbContext.GetAllStringPropertyLengths();
foreach (var constrainedField in stringlengthConstrainedField)
{
Console.WriteLine($"{constrainedField.EntityName} {constrainedField.PropertyName} {constrainedField.ColumnName} {constrainedField.MaxLength}");
}
Console.WriteLine("-------------- GET SPECIFIC ENTITY TYPE's FIELDS/COLUMS WITH A STRING LENGTH CONSTRAINT---------------");
var constrainedFieldsInSpecificTable = dbContext.GetStringPropertyLengths<Users>();
foreach (var constrainedField in constrainedFieldsInSpecificTable)
{
Console.WriteLine($"{constrainedField.EntityName} {constrainedField.PropertyName} {constrainedField.ColumnName} {constrainedField.MaxLength}");
}
}
🔎 LINQPad’s immediacy makes this kind of inspection extremely efficient.
DbContext Extension Methods
To keep the querying logic reusable and unobtrusive, the functionality is implemented as DbContext extension methods.
Enumerating all constrained string properties
public static class DbContextExtensions
{
public static IEnumerable<(string EntityName, string PropertyName, string ColumnName, int? MaxLength)>
GetAllStringPropertyLengths(this DbContext context, Type? specificEntityType = null)
{
var model = context.Model;
IEntityType? specificEntity = null;
if (specificEntityType != null)
{
specificEntity = model.FindEntityType(specificEntityType);
}
if (specificEntityType != null && specificEntity == null)
{
yield break;
}
var entityTypes = specificEntity == null
? model.GetEntityTypes()
: new[] { specificEntity };
foreach (var entityType in entityTypes)
{
var clrType = entityType.ClrType;
foreach (var property in entityType.GetProperties())
{
if (property.ClrType != typeof(string))
continue;
var maxLength = property.GetMaxLength();
if (maxLength == null)
continue;
var propertyName = property.Name;
var columnName = property.GetColumnName();
yield return (clrType.Name, propertyName, columnName, maxLength);
}
}
}
🧠 This method reports what EF Core actually enforces, not merely what was declared.
Strongly‑typed convenience overload
public static IEnumerable<(string EntityName, string PropertyName, string ColumnName, int? MaxLength)>
GetStringPropertyLengths<TModel>(this DbContext context)
{
var stringPropertyLenghtsForType =
GetAllStringPropertyLengths(context, typeof(TModel));
return stringPropertyLenghtsForType;
}
}
🎯 This keeps call‑sites expressive without duplicating logic.
Where This Is Useful
- ✅ Generating client‑side validation rules
- ✅ Auditing schema constraints in large models
- ✅ Verifying legacy databases after reverse engineering
- ✅ Debugging unexpected truncation or validation errors
- ✅ Building internal tooling around EF Core models
Because it relies on EF Core metadata, it remains accurate across migrations and configuration changes.
Closing Thoughts
EF Core already builds a comprehensive semantic model of your database schema. Exposing and inspecting that model directly is often simpler—and more reliable— than round‑tripping through reflection or database metadata tables.
LINQPad provides a particularly effective environment for this kind of work: quick, focused, and transparent.
📌 If you’re already using EF Core, you likely don’t need more tooling — you just need to look at the right layer.