Skip to main content

MongoDB Repository

Renamed: This project was renamed from Deveel.Repository to Kista on May 26, 2025. The name Kista is Old Norse for "chest" or "repository", better reflecting the project purpose as a data access framework.

FeatureStatusNotes
Base Repository
Filterable
QueryableVia MongoFramework IQueryable
Pageable
TrackingMongoFramework change tracking
Multi-tenantVia Kista.MongoFramework.MultiTenant

The MongoRepository<TEntity> class is an implementation of the repository pattern that stores entities in a MongoDB database, built on top of MongoFramework.

MongoFramework is a lightweight library that maps .NET objects to MongoDB documents using a design similar to Entity Framework Core.

Installation

dotnet add package Kista.MongoFramework

Registration

Use the fluent builder API to register the MongoDB driver:

// Program.cs
builder.Services.AddRepositoryContext()
.UseMongoDB<MyMongoContext>(b => b
.WithConnectionString("mongodb://localhost:27017/my_database"));

You can also use a builder delegate to configure the connection:

builder.Services.AddRepositoryContext()
.UseMongoDB<MyMongoContext>(b => b
.WithConnection(conn => conn.UseConnection("mongodb://localhost:27017/my_database")));

The following configuration methods are available on the MongoDB driver builder:

MethodDescription
WithConnectionString(string)Sets the MongoDB connection string.
WithConnection(Action<MongoConnectionBuilder>)Configures the connection using a builder delegate.
WithLifetime(ServiceLifetime)Sets the service lifetime (default: Scoped).
WithLifecycle()Enables lifecycle support (default: enabled)
WithoutLifecycle()Disables lifecycle support

Custom Context Type

If you derive from MongoDbContext (or MongoDbTenantContext for multi-tenant scenarios), register your concrete type:

builder.Services.AddRepositoryContext()
.UseMongoDB<MyMongoDbContext>(b => b
.WithConnectionString("mongodb://..."));

Multi-tenant Support

Multi-tenant support uses Finbuckle.MultiTenant. First, configure Finbuckle:

builder.Services.AddMultiTenant<MongoDbTenantInfo>()
.WithConfigurationStore()
.WithRouteStrategy("tenant");

Then register a tenant-aware MongoDB context (derived from MongoDbTenantContext) and the repository:

builder.Services.AddRepositoryContext()
.UseMongoDB<MyMongoTenantContext>(b => b
.WithConnection(conn => conn.UseConnection("mongodb://...")))
.WithMongoMultiTenancy<MongoDbTenantInfo>(defaultConnection: "mongodb://...");

The tenant context resolves the correct database connection for each tenant automatically.

Lifecycle Support

The MongoDB driver provides MongoRepositoryLifecycleHandler<TEntity> for lifecycle orchestration. It is enabled by default and can be disabled via .WithoutLifecycle().

Handler Behavior

OperationBehavior
ExistsAsyncLists collection names on the database to check whether the entity's collection exists.
CreateAsyncCreates the collection via CreateCollectionAsync, then builds all entity indexes defined in the MongoFramework entity mapping.
DropAsyncDrops all indexes, then drops the collection.
SeedAsyncInserts data directly into the raw MongoDB collection via InsertManyAsync / InsertOneAsync. Supports IEnumerable<TEntity>, IEnumerable<object>, and single entities.

Seeding Examples

Using a provider class:

public class ProductSeedProvider : IRepositorySeedDataProvider<Product> {
public IEnumerable<Product> GetSeedData() {
yield return new Product { Name = "Widget", Price = 9.99m };
yield return new Product { Name = "Gadget", Price = 24.99m };
}

IEnumerable<object> IRepositorySeedDataProvider.GetSeedData()
=> GetSeedData().Cast<object>();
}

// Program.cs
builder.Services.AddRepositoryContext()
.UseMongoDB<MyMongoContext>(b => b
.WithConnectionString("mongodb://..."))
.ConfigureLifecycle(options => {
options.SeedStrategy = SeedStrategy.Always;
})
.WithSeedData<Product, ProductSeedProvider>();

Using inline data (no provider class needed):

builder.Services.AddRepositoryContext()
.UseMongoDB<MyMongoContext>(b => b
.WithConnectionString("mongodb://..."))
.ConfigureLifecycle(options => {
options.SeedStrategy = SeedStrategy.Always;
})
.WithSeedData<Product>(new[] {
new Product { Name = "Widget", Price = 9.99m },
new Product { Name = "Gadget", Price = 24.99m }
});

The handler inserts the seed documents directly into the MongoDB collection via InsertManyAsync.

Disabling Lifecycle

builder.Services.AddRepositoryContext()
.UseMongoDB<MyMongoContext>(b => b
.WithConnectionString("mongodb://...")
.WithoutLifecycle());

Querying

MongoRepository<TEntity> implements IQueryableRepository<TEntity>, IFilterableRepository<TEntity>, and IPageableRepository<TEntity>.

With LINQ:

var items = repository.AsQueryable()
.Where(x => x.IsActive)
.OrderBy(x => x.Name)
.ToList();

With filter types:

// Lambda shorthand (extension method)
var items = await repository.FindAllAsync(x => x.IsActive);

// ExpressionQueryFilter
var filter = new ExpressionQueryFilter<MyEntity>(x => x.IsActive);
var items = await repository.FindAllAsync(new Query(filter));

// MongoDB-specific geo-distance filter
var geoFilter = new MongoGeoDistanceFilter(
fieldName: "Location",
center: new GeoPoint(lat, lon),
maxDistanceKm: 10);
var items = await repository.FindAllAsync(new Query(geoFilter));

Notes