EFCore.DataClassification
1.0.0
See the version list below for details.
dotnet add package EFCore.DataClassification --version 1.0.0
NuGet\Install-Package EFCore.DataClassification -Version 1.0.0
<PackageReference Include="EFCore.DataClassification" Version="1.0.0" />
<PackageVersion Include="EFCore.DataClassification" Version="1.0.0" />
<PackageReference Include="EFCore.DataClassification" />
paket add EFCore.DataClassification --version 1.0.0
#r "nuget: EFCore.DataClassification, 1.0.0"
#:package EFCore.DataClassification@1.0.0
#addin nuget:?package=EFCore.DataClassification&version=1.0.0
#tool nuget:?package=EFCore.DataClassification&version=1.0.0
EFCore.DataClassification
Overview
EFCore.DataClassification is a small extension library for Entity Framework Core 8 (EF Core 8) that adds SQL Server data classification support on top of the standard migrations pipeline.
It lets you:
- Annotate properties in your entity classes with a
[DataClassification]attribute. - Or configure classification via Fluent API (
HasDataClassification). - Automatically generate SQL Server metadata for:
ADD SENSITIVITY CLASSIFICATION(native SQL Server feature),- and
sp_addextendedproperty/sp_dropextendedpropertycalls for label, information type and rank.
- Keep the classification metadata in sync with EF Core migrations (add, remove, or change columns → classification migrations are generated accordingly).
- Validate rank values and label lengths at migration time with clear error messages.
The solution also includes:
EFCore.DataClassification.Tests– unit and integration tests for the library.EFCore.DataClassification.WebApi– a minimal ASP.NET Core Web API sample that demonstrates how to use the library in a real application.
Projects
EFCore.DataClassification- Core library.
- Contains attributes, annotations, extensions, SQL generator, custom migration operations and model differ.
EFCore.DataClassification.Tests- xUnit tests for attributes, extensions, SQL generator, and migration model differ.
EFCore.DataClassification.WebApi- Example ASP.NET Core 8 Web API application.
- Uses SQL Server + this library to demonstrate classification on a
Userand other sample entities.
Requirements
- .NET 8.0
- SQL Server 2017+:
- SQL Server 2019 & Azure SQL: Full support (Native Sensitivity Classification + Extended Properties).
- SQL Server 2017: Partial support (Extended Properties only). The library automatically detects the version and skips unsupported commands safely.
- Entity Framework Core 8 (
Microsoft.EntityFrameworkCore.SqlServer8.0.22)
Core Concepts
DataClassification constants and ranks
The library defines a central set of constants and valid rank values in DataClassificationConstants:
- Annotations (EF Core metadata keys):
DataClassification:LabelDataClassification:InformationTypeDataClassification:Rank
- Max lengths:
MaxLabelLength = 128MaxInformationTypeLength = 128
- Default schema:
dbo - Allowed ranks (maps to SQL Server ranks):
None,Low,Medium,High,Critical
It also exposes:
IsValidRank(string? rank)– checks if a rank is allowed.GetAllowedRanksString()– returns allowed ranks as comma-separated string for error messages.
SensitivityRank enum
The SensitivityRank enum lives in the Models folder:
public enum SensitivityRank
{
None,
Low,
Medium,
High,
Critical
}
This is the rank you use in attributes and Fluent API.
How It Works (High-Level)
You mark entity properties with:
[DataClassification(...)]attribute, orHasDataClassification(...)Fluent API.
Model building:
ModelBuilder.UseDataClassification()scans all entity types and their properties.- For each property with
[DataClassification], it writes EF Core annotations usingDataClassificationConstants.Label,InformationType, andRank.
Migrations model differ:
- During migrations diffing,
DataClassificationMigrationsModelDifferlooks at column mappings and checks whether they have classification annotations. - It adds custom migration operations:
CreateDataClassificationOperationRemoveDataClassificationOperation
- It also manages change detection and ordering, so when a column is dropped the classification remove operation is executed before the column drop.
- During migrations diffing,
SQL generation:
DataClassificationSqlGeneratorintercepts these custom operations and:- Writes extended properties with
sp_addextendedproperty/sp_dropextendedproperty(works on all SQL Server versions). - Writes SQL Server sensitivity classification using
ADD SENSITIVITY CLASSIFICATION ... WITH (LABEL = ..., INFORMATION_TYPE = ..., RANK = ...). - Automatically detects SQL Server version: Uses
SERVERPROPERTY('ProductMajorVersion')to check if the server supports sensitivity classification (SQL Server 2019+). On SQL Server 2017, only extended properties are written; sensitivity classification commands are safely skipped.
- Writes extended properties with
- It validates:
- Rank values (must be one of the allowed ranks).
- Label length (max 128 chars).
- On invalid configuration, it throws
DataClassificationExceptionwith a helpful message.
Design-time services:
DataClassificationDesignTimeServices(library) andDesign(WebApi) register:IMigrationsCodeGeneratorasDataClassificationMigrationsGenerator(adds needed namespaces to generated migrations).ICSharpMigrationOperationGeneratorasDataClassificationMigrationOperationGenerator(writes C# code for custom operations).
Installation & Setup
1. Add the library to your project
You can either:
- Reference the project directly from your solution:
- Add the existing
EFCore.DataClassificationproject to your solution. - Add a Project Reference to it from your application (Web, API, etc.).
- Add the existing
Or:
- (If packaged) add a NuGet package reference (not shown in this repo, but conceptually it would be something like
EFCore.DataClassification).
2. Configure DbContext options
In your application (for example in Program.cs):
builder.Services.AddDbContext<AppDbContext>(options =>
{
options
.UseSqlServer(connectionString)
.UseDataClassificationSqlServer(); // <-- enables library services
});
UseDataClassificationSqlServer():
- Registers
DataClassificationDbContextOptionsExtension. - That extension wires:
IMigrationsSqlGenerator→DataClassificationSqlGeneratorIMigrationsModelDiffer→DataClassificationMigrationsModelDiffer
3. Enable classification scanning in your DbContext
Inside your DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 1. Scan for [DataClassification] attributes
modelBuilder.UseDataClassification();
// 2. Optional Fluent API example
modelBuilder.Entity<User>()
.Property(u => u.PhoneNumber)
.HasDataClassification("Internal", "Phone Number", SensitivityRank.High);
}
Using the Attribute
DataClassificationAttribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class DataClassificationAttribute : Attribute
{
public string Label { get; }
public string InformationType { get; }
public SensitivityRank Rank { get; }
public DataClassificationAttribute(string label, string informationType, SensitivityRank rank)
{
Label = label;
InformationType = informationType;
Rank = rank;
}
}
Example – User entity
public class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Surname { get; set; } = string.Empty;
// Attribute-based classification
[DataClassification("Private", "Home Address", SensitivityRank.Medium)]
public string Adress { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
// Fluent API example is applied in OnModelCreating (PhoneNumber)
[DataClassification("Confidential", "Financial Information", SensitivityRank.High)]
public int Salary { get; set; }
[DataClassification("Confidential", "Admin Reference", SensitivityRank.High)]
public int? AdminId { get; set; }
public Admin? Admin { get; set; }
}
Another example – Customer:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
[DataClassification("Contact", "Email Address", SensitivityRank.High)]
public string Email { get; set; } = string.Empty;
[DataClassification("Address", "Mailing Address", SensitivityRank.None)]
public string Address { get; set; } = string.Empty;
}
After running migrations, these properties will have SQL Server sensitivity classification and extended properties attached.
Using Fluent API
You can also set classification via extensions on PropertyBuilder:
public static class PropertyBuilderExtensions
{
public static PropertyBuilder HasDataClassification(
this PropertyBuilder propertyBuilder,
string label,
string informationType,
SensitivityRank rank)
{ ... }
public static PropertyBuilder<TProperty> HasDataClassification<TProperty>(
this PropertyBuilder<TProperty> propertyBuilder,
string label,
string informationType,
SensitivityRank rank)
{ ... }
}
Example – from AppDbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.UseDataClassification();
modelBuilder.Entity<User>()
.Property(u => u.PhoneNumber)
.HasDataClassification("Internal", "Phone Number", SensitivityRank.High);
}
Migrations Support
Custom operations
The library introduces two custom migration operations:
CreateDataClassificationOperation- Used when a column with classification is added or when classification is changed.
RemoveDataClassificationOperation- Used when classification should be removed (for example when a column is dropped, unmapped, or its classification is removed/changed).
MigrationBuilder extensions
You can also explicitly add/remove classification in your migration code using MigrationBuilderExtensions:
public static class MigrationBuilderExtensions
{
public static OperationBuilder<CreateDataClassificationOperation> AddDataClassification(
this MigrationBuilder migrationBuilder,
string table,
string column,
string? schema = null,
string? label = null,
string? informationType = null,
string? rank = null);
public static OperationBuilder<RemoveDataClassificationOperation> DropDataClassification(
this MigrationBuilder migrationBuilder,
string table,
string column,
string? schema = null);
}
This is helpful if you want manual control over classification operations inside a particular migration.
SQL generation details
DataClassificationSqlGenerator:
- Validates incoming data:
- Allowed ranks (
None,Low,Medium,High,Critical). - Label length (
<= 128).
- Allowed ranks (
- Writes extended properties:
- Uses
sys.sp_addextendedpropertyandsys.sp_dropextendedproperty. - Properties:
DataClassification:LabelDataClassification:InformationTypeDataClassification:Rank
- Works on all SQL Server versions (2017+): Extended properties are always written regardless of SQL Server version.
- Uses
- Writes sensitivity classification:
- Uses
ADD SENSITIVITY CLASSIFICATION TO [schema].[table].[column] WITH (...). - Maps rank string values to SQL Server ranks:
"Low" → "LOW","Medium" → "MEDIUM","High" → "HIGH","Critical" → "CRITICAL".
"None"is treated as no sensitivity classification (no rank sent).- Version-aware execution: The generator automatically checks SQL Server version using
SERVERPROPERTY('ProductMajorVersion'):- SQL Server 2019+ (version 15+): Both extended properties AND native sensitivity classification are written.
- SQL Server 2017 (version 14): Only extended properties are written. Sensitivity classification commands are skipped safely (wrapped in
IF TRY_CONVERT(int, SERVERPROPERTY('ProductMajorVersion')) >= 15).
- This ensures migrations run successfully on both SQL Server 2017 and 2019+ without errors.
- Uses
If configuration is invalid, it throws DataClassificationException so you get a clear, early failure.
Web API Sample
The EFCore.DataClassification.WebApi project demonstrates an ASP.NET Core Web API using this library.
Key parts
Program- Configures
AppDbContextwith.UseDataClassificationSqlServer(). - Registers a global exception handler (
GlobalExceptionHandler). - Adds AutoMapper with
UserMappingProfile. - Enables Swagger/OpenAPI.
- Configures
AppDbContext- Defines multiple DbSets:
Users,Admins,Games,Car,Bikes,Homes,Customers,Documents, etc. - Calls
modelBuilder.UseDataClassification()inOnModelCreating. - Configures one property (
User.PhoneNumber) via Fluent APIHasDataClassification.
- Defines multiple DbSets:
Models
User,Customer,Admin,Game,Car,Bike,Home,Document, etc.- Several properties are decorated with
[DataClassification]to show different scenarios.
Controllers
UsersController:- Basic CRUD endpoints (
GET,POST,PUT,DELETE). - Additional queries (search, filter by admin).
- Uses DTOs and AutoMapper (
UserDtos,UserMappingProfile).
- Basic CRUD endpoints (
Middleware
GlobalExceptionHandler:- Implements
.NET 8 IExceptionHandlerpattern. - Maps:
DataClassificationException→ 400 Bad Request with specific error details.ArgumentNullException→ 400 Bad Request.InvalidOperationException→ 409 Conflict.- Any other exception → 500 Internal Server Error.
- Implements
Design-time
Designclass registers the design-time services for migrations generator and operation generator so thatdotnet efcommands produce migrations that understand the custom operations.
Running the Web API
- Configure a valid SQL Server connection string in
appsettings.json(e.g.DefaultConnection). - Run migrations, for example:
dotnet ef database update --project EFCore.DataClassification.WebApi - Start the API:
dotnet run --project EFCore.DataClassification.WebApi - Open Swagger UI (usually at
https://localhost:{port}/swagger) and test endpoints like:GET /api/usersPOST /api/usersGET /api/users/search?query=...
Tests
The EFCore.DataClassification.Tests project includes tests for:
- Attributes – e.g.
DataClassificationAttributeTestsconfirms:- Properties set correctly.
- Attribute is applicable to properties only.
AllowMultiple = false.
- Extensions – tests for:
ModelBuilderExtensions.UseDataClassification()– annotations applied correctly.PropertyBuilderExtensions.HasDataClassification()– annotation-based configuration works.MigrationBuilderExtensions– custom operations are added correctly.
- Infrastructure:
DataClassificationMigrationsModelDiffer:- Adds
CreateDataClassificationOperationfor new classified columns. - Adds
RemoveDataClassificationOperationfor removed/changed classification. - Sorts operations correctly when dropping columns.
- Adds
DataClassificationSqlGeneratorTests:- Generated SQL for extended properties and sensitivity classification.
- Integration tests:
- Verify end-to-end behavior from model configuration to generated migrations and SQL.
To run tests:
dotnet test
Error Handling and Validation
When classification configuration is invalid, the library throws DataClassificationException:
- Used in
DataClassificationSqlGenerator.ValidateDataClassification(...). - Scenarios:
- Rank not in allowed set.
- Label too long.
In the Web API sample:
GlobalExceptionHandlercatchesDataClassificationExceptionand returns a 400 Bad Request with a clear error message.- In development, additional details may also be exposed for other exception types.
Summary
EFCore.DataClassification is a focused EF Core 8 extension that:
- Adds an intuitive attribute + Fluent API for data classification.
- Integrates tightly with EF Core migrations and SQL Server sensitivity classification.
- Includes a ready-to-run Web API example and a rich test suite.
- Provides validation and clear error messages through
DataClassificationException.
License
This project is licensed under the MIT License - see the LICENSE file for details.
You are free to:
- ✅ Use commercially
- ✅ Modify
- ✅ Distribute
- ✅ Private use
- ✅ Sublicense
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.22)
- Microsoft.EntityFrameworkCore.SqlServer (>= 8.0.22)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.