eQuantic.Core.Data.MongoDb 5.6.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package eQuantic.Core.Data.MongoDb --version 5.6.0
                    
NuGet\Install-Package eQuantic.Core.Data.MongoDb -Version 5.6.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="eQuantic.Core.Data.MongoDb" Version="5.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="eQuantic.Core.Data.MongoDb" Version="5.6.0" />
                    
Directory.Packages.props
<PackageReference Include="eQuantic.Core.Data.MongoDb" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add eQuantic.Core.Data.MongoDb --version 5.6.0
                    
#r "nuget: eQuantic.Core.Data.MongoDb, 5.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package eQuantic.Core.Data.MongoDb@5.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=eQuantic.Core.Data.MongoDb&version=5.6.0
                    
Install as a Cake Addin
#tool nuget:?package=eQuantic.Core.Data.MongoDb&version=5.6.0
                    
Install as a Cake Tool

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 (OR across columns, !=, NULL, arbitrary predicates) run client-side over the fetched rows behind the explicit .AllowClientEvaluation() opt-in, with .AllowFiltering() gating scans. An OR whose 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 updatesUpdateMany(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/$pullAll on MongoDB (honouring [BsonElement] and custom serializers), PatchOperation.Increment/Add on Cosmos, collection col = 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 (Cassandra PagingState, Cosmos continuation tokens, MongoDB keyset by id), every page costing the same as the first, and IAsyncEnumerable<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) turns Modify into a conditional If-Match replace.
  • Prepared statements and LWT (Cassandra) — every repeated CQL text is prepared once per session and bound thereafter; AddIfNotExistsAsync runs INSERT … IF NOT EXISTS and reports whether it applied; WithConsistency(...) sets a per-query consistency level and CommitAsync(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's IServiceProvider (resolve the current tenant there), and IgnoringQueryFilters() 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.Data ActivitySource and 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

  • 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 Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
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 35 7/20/2026
5.2.0 38 7/20/2026
1.1.1 427 4/17/2024
1.1.0 474 2/12/2024
1.1.0-beta7 204 2/12/2024
1.1.0-beta6 239 1/19/2024
1.1.0-beta5 208 1/19/2024
1.1.0-beta4 208 1/17/2024
1.1.0-beta3 301 12/21/2023
1.1.0-beta2 210 12/21/2023
1.1.0-beta1 219 12/19/2023
1.0.0.3-beta6 651 7/5/2020
1.0.0.2-beta6 602 6/7/2020
1.0.0.1-beta6 627 6/7/2020
1.0.0.1-beta5 566 5/19/2020
1.0.0.1-beta4 584 5/16/2020
Loading failed

Native MongoDB Driver implementation of the eQuantic.Core.Data contracts