Indiko.Blocks.DataAccess.MongoDb 2.1.2

dotnet add package Indiko.Blocks.DataAccess.MongoDb --version 2.1.2
                    
NuGet\Install-Package Indiko.Blocks.DataAccess.MongoDb -Version 2.1.2
                    
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="Indiko.Blocks.DataAccess.MongoDb" Version="2.1.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Indiko.Blocks.DataAccess.MongoDb" Version="2.1.2" />
                    
Directory.Packages.props
<PackageReference Include="Indiko.Blocks.DataAccess.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 Indiko.Blocks.DataAccess.MongoDb --version 2.1.2
                    
#r "nuget: Indiko.Blocks.DataAccess.MongoDb, 2.1.2"
                    
#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 Indiko.Blocks.DataAccess.MongoDb@2.1.2
                    
#: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=Indiko.Blocks.DataAccess.MongoDb&version=2.1.2
                    
Install as a Cake Addin
#tool nuget:?package=Indiko.Blocks.DataAccess.MongoDb&version=2.1.2
                    
Install as a Cake Tool

Indiko.Blocks.DataAccess.MongoDb

MongoDB implementation of the Indiko data access abstractions, providing NoSQL document database support with full repository pattern.

Overview

This package provides a complete MongoDB implementation of the Indiko data access layer, supporting document-based data storage, indexing, aggregations, and transactions.

Features

  • MongoDB Driver: Full MongoDB.Driver integration
  • Repository Implementation: Complete IRepository<TEntity, TIdType> implementation
  • Document Storage: Native document/BSON support
  • Indexing: Attribute-based and fluent index configuration
  • Aggregation Pipeline: Support for MongoDB aggregation framework
  • Transactions: Multi-document ACID transactions
  • Change Streams: Real-time change notification support
  • GridFS: Large file storage support
  • Flexible Schema: Schema-less document storage
  • High Performance: Optimized for MongoDB operations

Installation

dotnet add package Indiko.Blocks.DataAccess.MongoDb

Quick Start

Define Your DbContext

using Indiko.Blocks.DataAccess.MongoDb;

public class AppDbContext : BaseDbContext
{
    public AppDbContext(IMongoDatabase database) : base(database)
    {
    }

    public IMongoCollection<User> Users => GetCollection<User>();
    public IMongoCollection<Order> Orders => GetCollection<Order>();

    protected override void OnModelCreating(MongoModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        // Configure collections
        modelBuilder.Entity<User>(entity =>
        {
            entity.ToCollection("users");
            entity.HasIndex(u => u.Email, unique: true);
        });

        modelBuilder.Entity<Order>(entity =>
        {
            entity.ToCollection("orders");
            entity.HasIndex(o => o.OrderNumber, unique: true);
            entity.HasIndex(o => o.UserId);
        });
    }
}

Configure Services

public class Startup : WebStartup
{
    public override void ConfigureServices(IServiceCollection services)
    {
        base.ConfigureServices(services);
        
        services.UseMongoDbDataAccess<AppDbContext>(options =>
        {
            options.ConnectionString = Configuration.GetConnectionString("MongoDB");
            options.DatabaseName = "MyAppDb";
        });
    }
}

Entity Definition

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

public class User : BaseEntity<ObjectId>
{
    [BsonElement("name")]
    public string Name { get; set; }
    
    [BsonElement("email")]
    public string Email { get; set; }
    
    [BsonElement("age")]
    public int Age { get; set; }
    
    [BsonElement("tags")]
    public List<string> Tags { get; set; }
    
    [BsonElement("metadata")]
    public Dictionary<string, object> Metadata { get; set; }
    
    [BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
    public DateTime BirthDate { get; set; }
}

Usage Examples

Basic CRUD

public class UserService
{
    private readonly IRepository<User, ObjectId> _userRepository;
    private readonly IUnitOfWork _unitOfWork;

    public UserService(IRepository<User, ObjectId> userRepository, IUnitOfWork unitOfWork)
    {
        _userRepository = userRepository;
        _unitOfWork = unitOfWork;
    }

    public async Task<User> CreateAsync(User user)
    {
        user.Id = ObjectId.GenerateNewId();
        await _userRepository.AddAsync(user);
        await _unitOfWork.SaveChangesAsync();
        return user;
    }

    public async Task<User> GetByIdAsync(ObjectId id)
    {
        return await _userRepository.ReadByIdAsync(id);
    }

    public async Task<IEnumerable<User>> SearchByNameAsync(string name)
    {
        return await _userRepository.ReadManyByQueryAsync(u => 
            u.Name.Contains(name, StringComparison.OrdinalIgnoreCase));
    }
}

MongoDB-Specific Queries

// Array contains
var usersWithTag = await _userRepository.ReadManyByQueryAsync(u => 
    u.Tags.Contains("premium"));

// Nested property queries
var activeAdults = await _userRepository.ReadManyByQueryAsync(u => 
    u.Age >= 18 && u.Status == Status.Active);

// Regular expressions
var emailPattern = new BsonRegularExpression("@gmail\\.com$", "i");
var gmailUsers = await _userRepository.AsQueryable()
    .Where(u => u.Email.Matches(emailPattern))
    .ToListAsync();

// GeoSpatial queries (if using geo indexes)
var nearbyUsers = await _userRepository.AsQueryable()
    .Where(u => u.Location.Near(longitude, latitude, maxDistance))
    .ToListAsync();

Indexing

Attribute-Based Indexing
using Indiko.Blocks.DataAccess.MongoDb.Attributes;

public class Product : BaseEntity<ObjectId>
{
    [MongoIndex(Unique = true)]
    public string SKU { get; set; }
    
    [MongoIndex]
    public string Category { get; set; }
    
    [MongoIndex(Name = "price_index", Order = -1)]
    public decimal Price { get; set; }
    
    // Compound index
    [MongoIndex(Name = "category_price", Order = 1, CompoundWith = nameof(Price))]
    public string CategoryForCompound { get; set; }
}
Fluent Index Configuration
protected override void OnModelCreating(MongoModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>(entity =>
    {
        entity.ToCollection("products");
        
        // Simple index
        entity.HasIndex(p => p.SKU, unique: true);
        
        // Compound index
        entity.HasIndex(
            p => new { p.Category, p.Price },
            name: "category_price_idx"
        );
        
        // Text index for full-text search
        entity.HasTextIndex(p => p.Description);
        
        // TTL index (auto-delete after expiration)
        entity.HasTTLIndex(p => p.ExpiresAt, expireAfterSeconds: 0);
    });
}

Aggregation Pipeline

// Using AsQueryable for aggregation
var topCategories = await _productRepository.AsQueryable()
    .GroupBy(p => p.Category)
    .Select(g => new 
    {
        Category = g.Key,
        Count = g.Count(),
        TotalRevenue = g.Sum(p => p.Price),
        AvgPrice = g.Average(p => p.Price)
    })
    .OrderByDescending(x => x.Count)
    .Take(10)
    .ToListAsync();

// Native aggregation pipeline
var collection = _context.GetCollection<Order>();
var pipeline = new[]
{
    new BsonDocument("$match", new BsonDocument("status", "completed")),
    new BsonDocument("$group", new BsonDocument
    {
        { "_id", "$userId" },
        { "totalOrders", new BsonDocument("$sum", 1) },
        { "totalAmount", new BsonDocument("$sum", "$amount") }
    }),
    new BsonDocument("$sort", new BsonDocument("totalAmount", -1)),
    new BsonDocument("$limit", 10)
};

var results = await collection.AggregateAsync<BsonDocument>(pipeline);

Transactions

public async Task TransferInventoryAsync(ObjectId fromWarehouseId, ObjectId toWarehouseId, string productId, int quantity)
{
    await _unitOfWork.BeginTransactionAsync();
    
    try
    {
        var warehouseRepo = _manager.GetRepository<Warehouse, ObjectId>();
        
        var fromWarehouse = await warehouseRepo.ReadByIdAsync(fromWarehouseId);
        var toWarehouse = await warehouseRepo.ReadByIdAsync(toWarehouseId);
        
        fromWarehouse.Inventory[productId] -= quantity;
        toWarehouse.Inventory[productId] += quantity;
        
        await warehouseRepo.UpdateAsync(fromWarehouse);
        await warehouseRepo.UpdateAsync(toWarehouse);
        
        await _unitOfWork.SaveChangesAsync();
        await _unitOfWork.CommitTransactionAsync();
    }
    catch
    {
        await _unitOfWork.RollbackTransactionAsync();
        throw;
    }
}

Change Streams

public async Task WatchForChangesAsync(CancellationToken cancellationToken)
{
    var collection = _context.GetCollection<User>();
    
    var pipeline = new EmptyPipelineDefinition<ChangeStreamDocument<User>>()
        .Match(change => change.OperationType == ChangeStreamOperationType.Insert ||
                        change.OperationType == ChangeStreamOperationType.Update);
    
    using var cursor = await collection.WatchAsync(pipeline, cancellationToken: cancellationToken);
    
    await cursor.ForEachAsync(change =>
    {
        Console.WriteLine($"Change detected: {change.OperationType}");
        Console.WriteLine($"Document: {change.FullDocument.Name}");
    }, cancellationToken);
}

Configuration

appsettings.json

{
  "ConnectionStrings": {
    "MongoDB": "mongodb://localhost:27017"
  },
  "MongoDb": {
    "DatabaseName": "MyAppDb",
    "MaxConnectionPoolSize": 500,
    "MinConnectionPoolSize": 10,
    "ConnectTimeout": "30s",
    "SocketTimeout": "30s",
    "ServerSelectionTimeout": "30s",
    "RetryWrites": true,
    "RetryReads": true
  }
}

Code Configuration

services.UseMongoDbDataAccess<AppDbContext>(options =>
{
    options.ConnectionString = "mongodb://localhost:27017";
    options.DatabaseName = "MyAppDb";
    options.MaxConnectionPoolSize = 500;
    options.EnableQueryLogging = env.IsDevelopment();
});

Best Practices

Use ObjectId for Primary Keys

public class MyEntity : BaseEntity<ObjectId>
{
    // ObjectId is optimized for MongoDB
}
public class Order : BaseEntity<ObjectId>
{
    public ObjectId UserId { get; set; }
    
    // Embedded document for better read performance
    public UserInfo UserInfo { get; set; }
    
    public List<OrderItem> Items { get; set; }
}

public class UserInfo
{
    public string Name { get; set; }
    public string Email { get; set; }
}

Use Projections

// Only retrieve needed fields
var userNames = await _userRepository.ReadByQueryWithSelectorAsync(
    where: u => u.IsActive,
    selector: u => new { u.Id, u.Name }
);

Create Appropriate Indexes

// Index frequently queried fields
entity.HasIndex(u => u.Email, unique: true);
entity.HasIndex(u => u.CreatedAt);
entity.HasIndex(u => new { u.Category, u.Status });

MongoDB-Specific Features

Document Validation

var validator = new BsonDocument("$jsonSchema", new BsonDocument
{
    { "bsonType", "object" },
    { "required", new BsonArray { "name", "email" } },
    { "properties", new BsonDocument
        {
            { "name", new BsonDocument("bsonType", "string") },
            { "email", new BsonDocument
                {
                    { "bsonType", "string" },
                    { "pattern", "^.+@.+$" }
                }
            }
        }
    }
});

GridFS for Large Files

public class FileService
{
    private readonly IGridFSBucket _gridFS;

    public async Task<ObjectId> UploadFileAsync(Stream fileStream, string filename)
    {
        return await _gridFS.UploadFromStreamAsync(filename, fileStream);
    }

    public async Task<byte[]> DownloadFileAsync(ObjectId fileId)
    {
        return await _gridFS.DownloadAsBytesAsync(fileId);
    }
}

Target Framework

  • .NET 10

Dependencies

  • Indiko.Blocks.DataAccess.Abstractions
  • MongoDB.Driver (2.28+)
  • MongoDB.Bson

License

See LICENSE file in the repository root.

  • Indiko.Blocks.DataAccess.Abstractions - Core data access abstractions
  • Indiko.Blocks.DataAccess.EntityFramework - EF Core implementation
  • Indiko.Blocks.DataAccess.Marten - Marten implementation
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
2.1.2 280 12/18/2025
2.1.1 674 12/2/2025
2.1.0 678 12/2/2025
2.0.0 318 9/17/2025
1.7.23 187 9/8/2025
1.7.22 187 9/8/2025
1.7.21 192 8/14/2025
1.7.20 194 6/23/2025
1.7.19 190 6/3/2025
1.7.18 186 5/29/2025
1.7.17 195 5/26/2025
1.7.15 153 4/12/2025
1.7.14 152 4/11/2025
1.7.13 161 3/29/2025
1.7.12 180 3/28/2025
1.7.11 182 3/28/2025
1.7.10 182 3/28/2025
1.7.9 171 3/28/2025
1.7.8 174 3/28/2025
1.7.5 193 3/17/2025
1.7.4 178 3/16/2025
1.7.3 179 3/16/2025
1.7.2 183 3/16/2025
1.7.1 210 3/11/2025
1.7.0 211 3/11/2025
1.6.8 211 3/11/2025
1.6.7 263 3/4/2025
1.6.6 154 2/26/2025
1.6.5 170 2/20/2025
1.6.4 165 2/20/2025
1.6.3 142 2/5/2025
1.6.2 136 1/24/2025
1.6.1 144 1/24/2025
1.6.0 155 1/16/2025
1.5.2 141 1/16/2025
1.5.1 161 11/3/2024
1.5.0 156 10/26/2024
1.3.2 152 10/24/2024
1.3.0 150 10/10/2024
1.2.5 158 10/9/2024
1.2.4 159 10/8/2024
1.2.1 151 10/3/2024
1.2.0 156 9/29/2024
1.1.1 149 9/23/2024
1.1.0 174 9/18/2024
1.0.33 158 9/15/2024
1.0.28 160 8/28/2024
1.0.27 190 8/24/2024
1.0.26 147 7/7/2024
1.0.25 164 7/6/2024
1.0.24 171 6/25/2024
1.0.23 173 6/1/2024
1.0.22 168 5/14/2024
1.0.21 166 5/14/2024
1.0.20 174 4/8/2024
1.0.19 171 4/3/2024
1.0.18 200 3/23/2024
1.0.17 195 3/19/2024
1.0.16 190 3/19/2024
1.0.15 166 3/11/2024
1.0.14 178 3/10/2024
1.0.13 181 3/6/2024
1.0.12 190 3/1/2024
1.0.11 186 3/1/2024
1.0.10 194 3/1/2024
1.0.9 186 3/1/2024
1.0.8 176 2/19/2024
1.0.7 185 2/17/2024
1.0.6 168 2/17/2024
1.0.5 168 2/17/2024
1.0.4 195 2/7/2024
1.0.3 181 2/6/2024
1.0.1 169 2/6/2024
1.0.0 226 1/9/2024
1.0.0-preview99 206 12/22/2023
1.0.0-preview98 158 12/21/2023
1.0.0-preview97 177 12/21/2023
1.0.0-preview96 161 12/20/2023
1.0.0-preview94 150 12/18/2023
1.0.0-preview93 225 12/13/2023
1.0.0-preview92 151 12/13/2023
1.0.0-preview91 167 12/12/2023
1.0.0-preview90 157 12/11/2023
1.0.0-preview89 175 12/11/2023
1.0.0-preview88 199 12/6/2023
1.0.0-preview87 184 12/6/2023
1.0.0-preview86 176 12/6/2023
1.0.0-preview85 169 12/6/2023
1.0.0-preview84 172 12/5/2023
1.0.0-preview83 173 12/5/2023
1.0.0-preview82 157 12/5/2023
1.0.0-preview81 165 12/4/2023
1.0.0-preview80 170 12/1/2023
1.0.0-preview77 165 12/1/2023
1.0.0-preview76 151 12/1/2023
1.0.0-preview75 157 12/1/2023
1.0.0-preview74 183 11/26/2023
1.0.0-preview73 187 11/7/2023
1.0.0-preview72 168 11/6/2023
1.0.0-preview71 178 11/3/2023
1.0.0-preview70 164 11/2/2023
1.0.0-preview69 163 11/2/2023
1.0.0-preview68 159 11/2/2023
1.0.0-preview67 159 11/2/2023
1.0.0-preview66 136 11/2/2023
1.0.0-preview65 171 11/2/2023
1.0.0-preview64 176 11/2/2023
1.0.0-preview63 159 11/2/2023
1.0.0-preview62 161 11/1/2023
1.0.0-preview61 155 11/1/2023
1.0.0-preview60 153 11/1/2023
1.0.0-preview59 155 11/1/2023
1.0.0-preview58 164 10/31/2023
1.0.0-preview57 150 10/31/2023
1.0.0-preview56 160 10/31/2023
1.0.0-preview55 156 10/31/2023
1.0.0-preview54 156 10/31/2023
1.0.0-preview53 145 10/31/2023
1.0.0-preview52 159 10/31/2023
1.0.0-preview51 153 10/31/2023
1.0.0-preview50 167 10/31/2023
1.0.0-preview48 173 10/31/2023
1.0.0-preview46 157 10/31/2023
1.0.0-preview45 154 10/31/2023
1.0.0-preview44 156 10/31/2023
1.0.0-preview43 156 10/31/2023
1.0.0-preview42 164 10/30/2023
1.0.0-preview41 172 10/30/2023
1.0.0-preview40 165 10/27/2023
1.0.0-preview39 169 10/27/2023
1.0.0-preview38 162 10/27/2023
1.0.0-preview37 161 10/27/2023
1.0.0-preview36 170 10/27/2023
1.0.0-preview35 164 10/27/2023
1.0.0-preview34 157 10/27/2023
1.0.0-preview33 167 10/26/2023
1.0.0-preview32 175 10/26/2023
1.0.0-preview31 170 10/26/2023
1.0.0-preview30 169 10/26/2023
1.0.0-preview29 173 10/26/2023
1.0.0-preview28 178 10/26/2023
1.0.0-preview27 167 10/26/2023
1.0.0-preview26 178 10/25/2023
1.0.0-preview25 177 10/23/2023
1.0.0-preview24 161 10/23/2023
1.0.0-preview23 166 10/23/2023
1.0.0-preview22 151 10/23/2023
1.0.0-preview21 170 10/23/2023
1.0.0-preview20 172 10/20/2023
1.0.0-preview19 172 10/19/2023
1.0.0-preview18 183 10/18/2023
1.0.0-preview16 178 10/11/2023
1.0.0-preview14 157 10/10/2023
1.0.0-preview13 173 10/10/2023
1.0.0-preview12 175 10/9/2023
1.0.0-preview11 167 10/9/2023
1.0.0-preview101 161 1/5/2024