Beyond Enums: Implementing the Smart Enum Pattern in .NET 10
Traditional enums work well for fixed values, but they cannot hold business rules or metadata. This article uses a Subscription Plan example to demonstrate how the Smart Enum Pattern creates a richer domain model.
Table of Contents
- The Problem
- Traditional Enum Limitations
- Introducing Smart Enums
- Adding Behavior
- Rich Metadata
- EF Core
- JSON
- Library vs Custom
- When to Use
- Common Mistakes
- Conclusion
The Problem with Traditional Enums
public enum SubscriptionPlan
{
Free,
Professional,
Enterprise
}
Business rules quickly spread across the application.
if(plan == SubscriptionPlan.Free)
{
maxUsers = 1;
storage = 1;
}
else if(plan == SubscriptionPlan.Professional)
{
maxUsers = 50;
storage = 500;
}
if or switch statements, the behavior belongs with the value itself.Traditional Enum Limitations
- No metadata
- No behavior
- Duplicated business logic
- Hard to extend
Introducing the Smart Enum Pattern
public sealed class SubscriptionPlan
{
public static readonly SubscriptionPlan Free =
new(1,"Free",1,1,false);
public static readonly SubscriptionPlan Professional =
new(2,"Professional",50,500,true);
public static readonly SubscriptionPlan Enterprise =
new(3,"Enterprise",9999,5000,true);
public int Id { get; }
public string Name { get; }
public int MaxUsers { get; }
public int StorageGB { get; }
public bool PrioritySupport { get; }
private SubscriptionPlan(
int id,
string name,
int maxUsers,
int storageGB,
bool prioritySupport)
{
Id=id;
Name=name;
MaxUsers=maxUsers;
StorageGB=storageGB;
PrioritySupport=prioritySupport;
}
}
Adding Behavior
public bool CanCreateProject(int currentUsers)
=> currentUsers < MaxUsers;
public bool HasPrioritySupport()
=> PrioritySupport;
var plan = SubscriptionPlan.Professional;
if(plan.CanCreateProject(25))
{
Console.WriteLine("Project created.");
}
Subscription Plan Example
| Plan | Users | Storage | Priority Support |
|---|---|---|---|
| Free | 1 | 1 GB | No |
| Professional | 50 | 500 GB | Yes |
| Enterprise | Unlimited | 5 TB | Yes |
Console.WriteLine(plan.MaxUsers);
Console.WriteLine(plan.StorageGB);
Console.WriteLine(plan.HasPrioritySupport());
EF Core Mapping
builder.Entity<Customer>()
.Property(x => x.Plan)
.HasConversion(
p => p.Id,
id => SubscriptionPlan.FromId(id));
JSON Serialization
Expose only the plan identifier or name in your API.
public record CustomerDto(
int Id,
string Plan);
Build Your Own or Use a Library?
Libraries such as Ardalis.SmartEnum provide parsing, equality, EF Core support, and serialization. Building one yourself is useful for learning or when custom behavior is required.
When to Use
- Subscription Plans
- Membership Levels
- Shipping Methods
- Order Status
- Payment Status
Common Mistakes
- Replacing every enum with a Smart Enum.
- Returning Smart Enums directly from APIs.
- Putting database or service calls inside a Smart Enum.
Conclusion
The Subscription Plan example demonstrates why Smart Enums are more expressive than traditional enums. Each plan encapsulates its own configuration and behavior, reducing duplicated business logic and making your domain model easier to understand, test, and extend.