eQuantic.Core.Data
5.6.1
See the version list below for details.
dotnet add package eQuantic.Core.Data --version 5.6.1
NuGet\Install-Package eQuantic.Core.Data -Version 5.6.1
<PackageReference Include="eQuantic.Core.Data" Version="5.6.1" />
<PackageVersion Include="eQuantic.Core.Data" Version="5.6.1" />
<PackageReference Include="eQuantic.Core.Data" />
paket add eQuantic.Core.Data --version 5.6.1
#r "nuget: eQuantic.Core.Data, 5.6.1"
#:package eQuantic.Core.Data@5.6.1
#addin nuget:?package=eQuantic.Core.Data&version=5.6.1
#tool nuget:?package=eQuantic.Core.Data&version=5.6.1
eQuantic.Core.Data
A provider-agnostic Repository + Unit of Work for .NET, where filtering, sorting and paging are authored typed and fluent — not as stringly-typed configuration.
var repo = unitOfWork.GetAsyncRepository<OrderData, Guid>();
var page = await repo.GetPagedAsync(
PageRequest.Of(pageIndex: 1, pageSize: 20),
new QueryOptions<OrderData>()
.Where(o => o.Total, FilterOperator.GreaterThan, 100m)
.And(o => o.Customer.Name, FilterOperator.Contains, term)
.OrderByDescending(o => o.CreatedAt)
.Include(nameof(OrderData.Customer))
.NoTracking());
// page is a PagedResult<OrderData>: Items + TotalCount + PageIndex/PageSize/PageCount + Has*Page
Why
The Repository pattern keeps your domain ignorant of the persistence engine — you code against
IRepository<TEntity, TKey>, and the Entity Framework (or other) provider supplies the
implementation. What usually rots is the query surface: dozens of GetPaged/GetFiltered
overloads and Action<Configuration> callbacks, with filters and sorts passed as magic strings.
eQuantic.Core.Data v5 collapses that: one method per operation, each taking a single
QueryOptions<TEntity> that you compose fluently and typed, backed by the
eQuantic.Linq query engine.
Getting started
This package is the contracts — pair it with a provider that supplies the engine:
dotnet add package eQuantic.Core.Data
dotnet add package eQuantic.Core.Data.EntityFramework.SqlServer # or PostgreSql / MySql / MongoDb / CosmosDb
Give your entities a key via IEntity<TKey> (the key is exposed through GetKey/SetKey — no mandated
Id property):
using eQuantic.Core.Data.Repository;
public class OrderData : IEntity<Guid>
{
public Guid Id { get; set; }
public decimal Total { get; set; }
public Guid GetKey() => Id;
public void SetKey(Guid key) => Id = key;
}
Register the provider's unit of work and repositories — the wiring is provider-specific (see the
Entity Framework Core provider) — then inject
IUnitOfWork, ask it for a repository, and query with a QueryOptions (the snippet at the top).
How you query
QueryOptions<TEntity> mirrors the eQuantic.Linq query builders, so filters read like code and
fail at compile time — not at runtime:
new QueryOptions<OrderData>()
.Where(o => o.Total, FilterOperator.GreaterThanOrEqual, 100m) // typed member selector
.And(o => o.Status, FilterOperator.Equal, OrderStatus.Paid) // clauses fold left to right:
.Or(o => o.Customer.IsVip, FilterOperator.Equal, true) // (total>=100 AND paid) OR vip
.OrderByDescending(o => o.CreatedAt)
.ThenBy("customer.name"); // string path for dynamic columns
You reach for whichever filter form fits — all end up as one predicate the provider translates:
| Form | When |
|---|---|
Where(selector, op, value) / And / Or |
Primary — typed, fluent, compile-checked. |
Where(string path, op, value) |
Dynamic column names; operator and value stay typed. |
Where(ISpecification<T>) |
A reusable domain rule (specification pattern). |
Where(Expression<Func<T, bool>>) |
An arbitrary predicate you already hold. |
Where(ExpressionModel<T>) |
A serialized filter — built in code or received over the wire. |
Where("total:gt(100)") |
The boundary where a filter arrives as a query string (e.g. a filterBy parameter). Prefer the typed form in code. |
The query-string grammar behind the string forms (total:gt(100),status:eq(Paid)) is documented in
the eQuantic.Linq query-string reference.
Paging that tells you what you got
PagedResult<OrderData> page = await repo.GetPagedAsync(PageRequest.Of(2, 20), options);
// page.Items, page.TotalCount, page.PageIndex, page.PageSize, page.PageCount,
// page.HasPreviousPage, page.HasNextPage
PageRequest is one-based (Skip/Take derived); PagedResult<T> carries the items and the
totals — no second count call, no bare IEnumerable<T>.
Provider-agnostic by design
This package is the contracts (IRepository, IUnitOfWork, QueryOptions, PageRequest,
PagedResult, specifications). The persistence engine is kept out of the type signatures —
IRepository<TEntity, TKey>, not IRepository<TUnitOfWork, TEntity, TKey>. The Entity Framework
implementation lives in the provider packages (eQuantic.Core.Data.EntityFramework and friends).
Native providers — MongoDB, Azure Cosmos DB, Apache Cassandra
The same contracts implemented directly on each official driver — no Entity Framework — with a
lean write model: Add/Modify/Remove buffer typed writes and one native batch runs on Commit
(no change tracking or snapshotting). Explicit transactions are opt-in: multi-document sessions on
MongoDB (reads inside the transaction see its writes), a single-partition TransactionalBatch on
Cosmos, an atomic LOGGED BATCH on Cassandra. All three ship the same fluent, typed migrations,
authored with member selectors instead of field strings:
services.AddMongoRepositories("mongodb://localhost:27017", "shop");
services.AddMongoMigrations(typeof(AddProductIndexes).Assembly);
[Migration("Product indexes", 2026, 7, 20, 14, 0, 0)]
public sealed class AddProductIndexes : Migration
{
public override void Up(IMigrationBuilder migration) =>
migration.For<Product>(product => product
.EnsureCollection()
.Index(x => x.Category)
.CompositeIndex(keys => keys.Descending(x => x.Price).Ascending(x => x.Name)));
}
// on startup: apply pending migrations, once each
await serviceProvider.GetRequiredService<IMigrationRunner>().RunAsync();
The engine — what the providers add on top of the drivers
- Pushdown + residual filtering (Cassandra) — every clause CQL can express runs on the cluster;
the ones it cannot (
ORacross columns,!=,NULL, arbitrary predicates) run client-side over the fetched rows behind the explicit.AllowClientEvaluation()opt-in, with.AllowFiltering()gating scans. AnORwhose branches each pin a partition needs no opt-in at all: it runs as parallel native queries, merged and de-duplicated by primary key. Explain()on every provider (IExplainableRepository<T>) — the native statement (CQL, Cosmos SQL, aggregation pipeline), what is pushed down versus evaluated client-side, partition scoping, and the opt-ins a query still needs. Builds the plan without executing anything.- Computed set-based updates —
UpdateMany(filter, x => new E { N = x.N + 1, Tags = x.Tags.Append("vip").ToList() })renders to the store's atomic form:$inc/$mul/$push/$addToSet/$pullAllon MongoDB (honouring[BsonElement]and custom serializers),PatchOperation.Increment/Addon Cosmos, collectioncol = col + ?and counter columns on Cassandra. Shapes a store cannot apply atomically are rejected with the reason — an update never silently degrades to read-modify-write. - Continuation paging and streaming (
IContinuationReadRepository<T>,IStreamingReadRepository<T>) — deep pages walk the native path (CassandraPagingState, Cosmos continuation tokens, MongoDB keyset by id), every page costing the same as the first, andIAsyncEnumerable<T>streams hold one page in memory at a time. - Partition-key inference and ETag concurrency (Cosmos) — a filter that pins the partition key
scopes the query to a single partition automatically (the biggest RU saving there is), and
ConcurrencyToken(x => x.ETag)turnsModifyinto a conditionalIf-Matchreplace. - Prepared statements and LWT (Cassandra) — every repeated CQL text is prepared once per
session and bound thereafter;
AddIfNotExistsAsyncrunsINSERT … IF NOT EXISTSand reports whether it applied;WithConsistency(...)sets a per-query consistency level andCommitAsync(o => o.WithTtl(...))writes expiring rows. - Global query filters (multi-tenancy) — register
new QueryFilters().For<Order>(...)as a singleton and every read and set-based write is scoped by it on all three providers; a per-request factory receives the scope'sIServiceProvider(resolve the current tenant there), andIgnoringQueryFilters()opts a read out. A tenant filter on the Cosmos partition key auto-scopes the query to that partition. - OpenTelemetry built in — subscribe to the
eQuantic.Core.DataActivitySourceand every provider emits spans with the statement (placeholders, never values) plus the engine's own facts: client evaluation, split-query count, partition scoping, staged write counts.
The rule behind all of it: a query never lies about its cost. Anything beyond the store's
native access path is explicit and opt-in, and Explain() shows exactly what will run. Providers
target net10.0.
Install
dotnet add package eQuantic.Core.Data
Targets net8.0 and net10.0. Depends only on the framework-free
eQuantic.Linq.Web and
eQuantic.Linq.Specification.
Learn more
- Upgrading to v5.6 — the behaviour corrections in 5.6.0 and what they mean for code written against 5.4/5.5.
- Repository Pattern walkthrough — a full example: data entities, unit of work, repository, specifications and domain services.
- v5 contracts design — the rationale, the consolidated interface and the breaking-change/migration summary.
- Releasing — the automated release flow (maintainers).
Upgrading to v5
v5 is a deliberate breaking redesign: one QueryOptions argument per read (not
Action<Configuration> + overloads), PagedResult<T> paging, the unit-of-work type parameter
removed from IRepository/GetRepository, an IEntity<TKey> constraint (no new()), the
SQL/relational surface moved to the provider layer, and the monolithic eQuantic.Linq dependency
replaced by eQuantic.Linq.Web + eQuantic.Linq.Specification. The full before/after is in the
design doc.
MIT © eQuantic Tech
| 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 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
- eQuantic.Linq.Expressions (>= 3.7.1)
- eQuantic.Linq.Specification (>= 3.7.1)
- eQuantic.Linq.Web (>= 3.7.1)
-
net8.0
- eQuantic.Linq.Expressions (>= 3.7.1)
- eQuantic.Linq.Specification (>= 3.7.1)
- eQuantic.Linq.Web (>= 3.7.1)
NuGet packages (11)
Showing the top 5 NuGet packages that depend on eQuantic.Core.Data:
| Package | Downloads |
|---|---|
|
eQuantic.Core.Data.EntityFramework
Core Data library for Entity Framework |
|
|
eQuantic.Core.Data.EntityFramework.SqlServer
Core Data library for Entity Framework and SQL Server |
|
|
eQuantic.Core.Data.MongoDb
Native MongoDB implementation of the eQuantic.Core.Data contracts — the MongoDB Driver directly, no Entity Framework, with first-class document-store migrations. |
|
|
eQuantic.Core.Data.EntityFramework.MongoDb
Core Data library for Entity Framework and Mongo DB |
|
|
eQuantic.Core.Data.EntityFramework.MySql
Core Data library for Entity Framework and MySQL |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.7.0 | 0 | 7/22/2026 |
| 5.6.1 | 0 | 7/21/2026 |
| 5.6.0 | 0 | 7/21/2026 |
| 5.5.0 | 0 | 7/21/2026 |
| 5.4.0 | 0 | 7/21/2026 |
| 5.3.0 | 41 | 7/20/2026 |
| 5.2.0 | 37 | 7/20/2026 |
| 5.1.0 | 154 | 7/19/2026 |
| 5.0.0 | 53 | 7/19/2026 |
| 4.3.2 | 880 | 3/2/2026 |
| 4.3.1 | 512 | 3/2/2026 |
| 4.3.0 | 529 | 2/17/2026 |
| 4.2.3 | 8,794 | 8/25/2024 |
| 4.2.2 | 1,763 | 7/22/2024 |
| 4.2.1 | 6,198 | 1/8/2024 |
| 4.2.0 | 2,338 | 12/20/2023 |
| 4.1.1 | 1,518 | 11/18/2023 |
| 4.1.0 | 1,365 | 10/21/2023 |
| 4.0.3 | 1,969 | 8/13/2023 |
| 4.0.2 | 854 | 8/13/2023 |
Entity ignorant persistance with Repository Pattern