TinyHelpers.EntityFrameworkCore
3.0.78
dotnet add package TinyHelpers.EntityFrameworkCore --version 3.0.78
NuGet\Install-Package TinyHelpers.EntityFrameworkCore -Version 3.0.78
<PackageReference Include="TinyHelpers.EntityFrameworkCore" Version="3.0.78" />
<PackageVersion Include="TinyHelpers.EntityFrameworkCore" Version="3.0.78" />
<PackageReference Include="TinyHelpers.EntityFrameworkCore" />
paket add TinyHelpers.EntityFrameworkCore --version 3.0.78
#r "nuget: TinyHelpers.EntityFrameworkCore, 3.0.78"
#:package TinyHelpers.EntityFrameworkCore@3.0.78
#addin nuget:?package=TinyHelpers.EntityFrameworkCore&version=3.0.78
#tool nuget:?package=TinyHelpers.EntityFrameworkCore&version=3.0.78
Tiny Helpers for Entity Framework Core
TinyHelpers.EntityFrameworkCore is a small collection of practical helpers for Entity Framework Core applications: value converters, value comparers, global query filters, transaction helpers, and vector-column mapping.
The package is designed to reduce repetitive configuration and keep the persistence intent of your Entity Framework Core model close to the entity type or model configuration that uses it.
Compatibility
The package targets:
- .NET 8
- .NET 9
- .NET 10
Some features are framework-specific:
- Named query filters are available only on .NET 10+
- The package focuses on Entity Framework Core model configuration, conversions, and transaction helpers
Installation
Install the package from NuGet:
dotnet add package TinyHelpers.EntityFrameworkCore
Or search for TinyHelpers.EntityFrameworkCore in the Visual Studio Package Manager.
Contents
- Converters and comparers
- Property builder helpers
- Query filters
- Transaction helpers
- Vector columns
- Quick examples
- Contribute
Converters and comparers
This area contains helpers for storing common CLR shapes in a single database column while keeping Entity Framework Core change tracking aligned with the persisted representation.
JsonStringConverter<T>
Converts a CLR object graph to and from a JSON string for storage in a text column.
Use it when a value object or small object graph belongs to the entity and should live in one text column without manually handling serialization in every entity configuration.
JsonStringComparer<T>
Compares values by their JSON representation.
Use it together with JsonStringConverter<T> so Entity Framework Core detects changes based on serialized content instead of object reference identity, avoiding redundant updates when two values serialize to the same payload.
StringArrayConverter
Converts a sequence of strings to a single delimiter-separated value.
Use it when a small string collection should stay on the entity row and does not need independent relational querying.
StringArrayComparer
Compares string sequences by content and order.
Use it together with StringArrayConverter so Entity Framework Core treats two collections as equal when they contain the same values in the same order, even when the collection instances are different.
StringEmptyToNullConverter
Normalizes blank strings to null before persistence.
Use it when an empty or whitespace-only value should be treated as the absence of data.
StringEmptyToNullTrimConverter
Normalizes blank strings to null and trims meaningful values before persistence.
Use it when user input should not preserve incidental leading or trailing whitespace.
Property builder helpers
PropertyBuilderExtensions
These helpers keep conversion and comparison pieces together so model configuration can state persistence intent once while Entity Framework Core still receives the metadata it needs for materialization and change tracking.
| Method | What it does | When to use it |
|---|---|---|
HasJsonConversion<T>(this PropertyBuilder<T?>, JsonSerializerOptions?, bool useUtcDate, bool serializeEnumAsString) |
Stores a property as JSON and wires up matching serialized-value change tracking. | When a value object or small object graph should be persisted in a single text column. |
HasArrayConversion(this PropertyBuilder<IEnumerable<string>>) |
Stores a string sequence as a single delimited column and tracks sequence content. | When the property is exposed as IEnumerable<string> and does not need a separate relational table. |
HasArrayConversion(this PropertyBuilder<string[]>) |
Stores a string array as a single delimited column and tracks sequence content. | When the property is exposed as string[] and does not need a separate relational table. |
IsVector(this PropertyBuilder, int size = 1536) |
Maps the property to a vector column type with the specified dimension. | When the database supports vector search or embeddings. |
IsVector<T>(this PropertyBuilder<T>, int size = 1536) |
Strongly typed version of IsVector. |
When the property is strongly typed and you want fluent chaining. |
Example
using Microsoft.EntityFrameworkCore;
using TinyHelpers.EntityFrameworkCore.Extensions;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>(builder =>
{
builder.Property(x => x.Metadata).HasJsonConversion();
builder.Property(x => x.Tags).HasArrayConversion(",");
builder.Property(x => x.Embedding).IsVector(1536);
});
}
Query filters
ModelBuilderExtensions
These helpers centralize model-wide conventions such as soft delete, tenant isolation, or visibility rules so each entity type does not need duplicate configuration.
| Method | What it does | When to use it |
|---|---|---|
ApplyQueryFilter<TEntity>(Expression<Func<TEntity, bool>>) |
Applies the same filter to all mapped entity types assignable to TEntity. |
When several entities share a common base type or interface. |
ApplyQueryFilter<TType>(string propertyName, TType value) |
Applies a filter to all mapped entity types that expose a property with the given name and type. | When entities do not share a type but expose the same shadow or CLR property. |
ApplyQueryFilter<TEntity>(string filterName, Expression<Func<TEntity, bool>>) |
.NET 10+ named filter overload. | When you want to selectively disable one filter later, such as soft delete without disabling tenant isolation. |
ApplyQueryFilter<TType>(string filterName, string propertyName, TType value) |
.NET 10+ named filter overload for a property match. | When a shared property drives a filter that may need to be disabled independently. |
GetEntityTypes<TType>() |
Returns the mapped CLR entity types assignable to TType. |
When you need the model types behind a base type or interface. |
GetEntityTypes(Type baseType) |
Returns the mapped CLR entity types assignable to a runtime type. | When the target type is only known at runtime. |
Example
using Microsoft.EntityFrameworkCore;
using TinyHelpers.EntityFrameworkCore.Extensions;
public abstract class DeletableEntity
{
public bool IsDeleted { get; set; }
}
public interface ISoftDeletable
{
bool IsDeleted { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyQueryFilter<DeletableEntity>(e => !e.IsDeleted);
modelBuilder.ApplyQueryFilter<ISoftDeletable>(e => !e.IsDeleted);
}
.NET 10 named filters
Starting with .NET 10, named filters let you attach a name to each query filter and disable only the ones you need:
modelBuilder.ApplyQueryFilter<ISoftDeletable>("SoftDelete", e => !e.IsDeleted);
modelBuilder.ApplyQueryFilter<ITenantEntity>("TenantFilter", e => e.TenantId == currentTenantId);
Later, you can disable just one filter:
var items = await context.Set<Order>().IgnoreQueryFilters(["SoftDelete"]).ToListAsync();
Transaction helpers
DbContextExtensions
These helpers wrap Entity Framework Core execution strategies and explicit transactions so retry behavior stays consistent and transaction boilerplate stays out of repositories and services.
| Method | What it does | When to use it |
|---|---|---|
ExecuteTransactionAsync(Func<CancellationToken, Task>) |
Runs work inside a transaction and commits when the action completes. | When you do not need the transaction object itself. |
ExecuteTransactionAsync<TResult>(Func<CancellationToken, Task<TResult>>) |
Same as above, but returns a result. | When the unit of work produces a value. |
ExecuteTransactionAsync(Func<IDbContextTransaction, CancellationToken, Task>) |
Runs work inside a transaction and passes the active transaction to the callback. The callback decides whether and when to commit it. | When lower-level APIs need direct transaction access or custom commit timing. |
ExecuteTransactionAsync<TResult>(Func<IDbContextTransaction, CancellationToken, Task<TResult>>) |
Same as above, but returns a result. | When you need both transaction access, custom commit timing, and a computed value. |
Example
using TinyHelpers.EntityFrameworkCore.Extensions;
await context.ExecuteTransactionAsync(async cancellationToken =>
{
context.Add(new Order { Id = 1 });
await context.SaveChangesAsync(cancellationToken);
});
Vector columns
VectorAttribute
Marks a property or field as a vector column.
Use it when your database provider supports vector types and you want the mapping intent to live directly on the entity member.
Example
using System.ComponentModel.DataAnnotations.Schema;
public sealed class Document
{
public int Id { get; set; }
[Vector(1536)]
public float[] Embedding { get; set; } = [];
}
Quick examples
JSON-backed property mapping
using Microsoft.EntityFrameworkCore;
using TinyHelpers.EntityFrameworkCore.Extensions;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>(builder =>
{
builder.Property(x => x.Metadata).HasJsonConversion();
});
}
String collection mapping
using Microsoft.EntityFrameworkCore;
using TinyHelpers.EntityFrameworkCore.Extensions;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>(builder =>
{
builder.Property(x => x.Tags).HasArrayConversion(",");
});
}
Global soft-delete filter
using Microsoft.EntityFrameworkCore;
using TinyHelpers.EntityFrameworkCore.Extensions;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyQueryFilter<ISoftDeletable>(e => !e.IsDeleted);
}
Contribute
The project is continuously evolving. Contributions, issues, and pull requests are welcome.
Work on the develop branch, not on master. Pull requests should target develop.
| 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 is compatible. 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 is compatible. 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. |
-
net10.0
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.9)
- TinyHelpers (>= 3.3.26)
-
net8.0
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.28)
- TinyHelpers (>= 3.3.26)
-
net9.0
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.17)
- TinyHelpers (>= 3.3.26)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.78 | 0 | 6/18/2026 |
| 3.0.76 | 101 | 6/10/2026 |
| 3.0.71 | 125 | 5/22/2026 |
| 3.0.70 | 144 | 4/15/2026 |
| 3.0.69 | 547 | 3/12/2026 |
| 3.0.65 | 278 | 3/11/2026 |
| 3.0.64 | 305 | 2/11/2026 |
| 3.0.62 | 280 | 1/14/2026 |
| 3.0.61 | 520 | 12/10/2025 |
| 3.0.60 | 620 | 11/13/2025 |
| 3.0.58 | 1,708 | 10/15/2025 |
| 3.0.56 | 10,354 | 9/10/2025 |
| 3.0.54 | 699 | 9/1/2025 |
| 3.0.52 | 705 | 7/30/2025 |
| 3.0.51 | 1,206 | 7/9/2025 |
| 3.0.49 | 643 | 6/11/2025 |
| 3.0.48 | 1,922 | 5/14/2025 |
| 3.0.47 | 2,574 | 4/9/2025 |
| 3.0.46 | 559 | 3/18/2025 |
| 3.0.45 | 350 | 3/12/2025 |