MongoFlow 0.2.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package MongoFlow --version 0.2.0                
NuGet\Install-Package MongoFlow -Version 0.2.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="MongoFlow" Version="0.2.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add MongoFlow --version 0.2.0                
#r "nuget: MongoFlow, 0.2.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.
// Install MongoFlow as a Cake Addin
#addin nuget:?package=MongoFlow&version=0.2.0

// Install MongoFlow as a Cake Tool
#tool nuget:?package=MongoFlow&version=0.2.0                

MongoFlow

License: MIT .NET C# NuGet

[!WARNING] This package is not ready for production use. It is still in development and should not be used in a production environment.

We welcome your feedback! You can reach us by opening a GitHub issue.

MongoFlow is a lightweight MongoDB toolkit for .NET Core, built on top of the official MongoDB.Driver. It simplifies database operations with features like Unit of work & Repository pattern, query filters, interceptors, and more.

Features

  • 🔄 Unit of Work & Repository patterns
  • 🎯 Interceptors for custom logic
  • 🔍 Query filters with LINQ support
  • 🗑️ Built-in soft delete support
  • 🏢 Multi-tenant support
  • ⚡ Transaction support

Getting Started

Installation

To install MongoFlow, add the following package to your project:

dotnet add package MongoFlow

Create Your Vault and Models

public class BloggingVault : MongoVault
{
    public BloggingVault(VaultConfigurationManager<BloggingVault> configurationManager) : base(configurationManager)
    {
    }

    public DocumentSet<Blog> Blogs { get; set; } = null!;
}

public class Blog
{
    public ObjectId Id { get; init; }
    public required string Title { get; set; }
    public required string Content { get; set; }
    public bool Deleted { get; set; }
    public int TenantId { get; set; }
}

Create Configuration

public sealed class BloggingConfiguration : IVaultConfigurationSpecification
{
    private readonly IMongoDatabase _db;

    // You can inject singleton services here
    public BloggingConfiguration(IMongoDatabase db)
    {
        _db = db;
    }

    public void Configure(VaultConfigurationBuilder builder)
    {
        builder.SetDatabase(_db);
    }
}

Register MongoVault

MongoVault is registered as a scoped service, so you can inject it into a service.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IMongoDatabase>(_ =>
{
    var client = new MongoClient("mongodb://localhost:27017");
    return client.GetDatabase("Blogging");
});

// You can combine multiple specifications
builder.Services.AddMongoVault<BloggingVault>(x => x.AddSpecification<BloggingConfiguration>());

Basic MongoVault Usage

public class BlogService
{
    private readonly BloggingVault _vault;

    public BlogService(BloggingVault vault)
    {
        _vault = vault;
    }

    public async Task<Blog> GetBlogAsync(ObjectId id)
    {
        return await _vault.Blogs.Find(x => x.Id == id).FirstOrDefaultAsync();
    }

    public async Task AddBlogAsync(Blog blog)
    {
        _vault.Blogs.Add(blog);
        await _vault.SaveAsync();
    }
}

Add Documents

MongoVault is unit of work, so you can add, update or delete documents to the vault and save them all at once with a single transaction.

var blog1 = new Blog
{
    Title = "Hello World",
    Content = "This is a blog post",
    TenantId = 1
};
var blog2 = new Blog
{
    Title = "Hello World 2",
    Content = "This is a blog post 2",
    TenantId = 2
};

vault.Blogs.Add(blog1); // It doesn't save to the database yet
vault.Blogs.Add(blog2)

await vault.SaveAsync(); // Saves all changes in a single transaction

Query Documents

Because MongoFlow is built on top of MongoDB.Driver, you can use LINQ, Find, Aggregate, etc. to query documents.

// Query documents using LINQ
var blogsWithLinq = await vault.Blogs
    .AsQueryable()
    .Where(x => x.Title.Contains("Hello"))
    .ToListAsync();

// Query documents using Find
var blogsWithFind = await vault.Blogs
    .Find(x => x.Title.Contains("Hello"))
    .ToListAsync();

// Query documents using Aggregate
var blogsWithAggregate = await vault.Blogs
    .Aggregate()
    .Match(x => x.Title.Contains("Hello"))
    .ToListAsync();

Also you can access the underlying MongoDB.Driver.IMongoCollection<T>, but it's not recommended because MongoFlow has some features that are not supported by MongoDB.Driver.IMongoCollection<T> like query filters, interceptors, etc.

var collection = vault.Blogs.Collection;

Interceptors

MongoFlow has interceptors to intercept the operations before or after they are sent to the database.

public class MyInterceptor : VaultInterceptor
{
    private readonly MyService _myService;

    // You can inject services here
    public MyInterceptor(MyService myService)
    {
        _myService = myService;
    }

    // This method is called before the changes are saved to the database
    public override async ValueTask SavingChangesAsync(VaultInterceptorContext context, CancellationToken cancellationToken)
    {
        MongoVault vault = context.Vault; // You can get the vault instance
        List<VaultOperation> operations = context.Operations; // You can get and write operations that will be saved to the database
        IClientSessionHandle session = context.Session; // You can get the current session that is used to save the changes

        await _myService.DoSomethingBeforeSaveAsync();
    }

    // This method is called after the changes are saved to the database
    public override async ValueTask SavedChangesAsync(VaultInterceptorContext context, int result, CancellationToken cancellationToken)
    {
        await _myService.DoSomethingAfterSaveAsync();
    }

    // This method is called when an exception is thrown
    public override async ValueTask SaveChangesFailedAsync(Exception exception, VaultInterceptorContext context, CancellationToken cancellationToken)
    {
        await _myService.DoSomethingOnExceptionAsync(exception);
    }
}

Then you can register the interceptor in the VaultConfigurationBuilder:

public class BloggingConfiguration : IVaultConfigurationSpecification
{
    public void Configure(VaultConfigurationBuilder builder)
    {
        builder.AddInterceptor<MyInterceptor>(); 
        // or builder.AddInterceptor(new MyInterceptor());
    }
}

Query Filters

MongoFlow has built-in query filters to filter documents. Common use cases are soft delete and multi-tenancy which are supported out of the box.

To configure basic query filters, you can enable with AddQueryFilter method in IVaultConfigurationSpecification:

public class BloggingConfiguration : IVaultConfigurationSpecification
{
    public void Configure(VaultConfigurationBuilder builder)
    {
        builder.ConfigureDocumentType<Blog>(blogBuilder =>
        {
            // Static query filter
            blogBuilder.AddQueryFilter(x => !x.Deleted);

            // You can use IServiceProvider to resolve services
            blogBuilder.AddQueryFilter(services =>
                x => x.TenantId == services.GetRequiredService<ITenantProvider>().TenantId);
        });
    }
}

You can ignore query filters by calling IgnoreQueryFilters method on DocumentSet<T>:

vault.Blogs.IgnoreQueryFilter().Find(x => x.TenantId == 1).ToListAsync();

If you want to add query filters to all document types that are implemented by an interface, you can use AddMultiQueryFilters method:

// This will add !x.Deleted to all document types that implement ISoftDelete
builder.AddMultiQueryFilters<ISoftDelete>(x => !x.Deleted);

// This will add to all document types that implement IMultiTenant
builder.AddMultiQueryFilters<IMultiTenant>(services => x => x.TenantId == services.GetRequiredService<ITenantProvider>().TenantId);

Soft Delete

You can enable soft delete support with interceptors and query filters easily. However, MongoFlow supports soft delete out of the box.

To enable soft delete support, you can use AddSoftDelete method in IVaultConfigurationSpecification:

builder.AddSoftDelete(new VaultSoftDeleteOptions<ISoftDelete>
{
    IsDeletedAccessor = x => x.Deleted,
    ChangeIsDeleted = (entity, isDeleted) => entity.Deleted = isDeleted
});

Now it is done! AddSoftDelete adds a query filter and interceptor to all document types that implement ISoftDelete.

Multi-tenancy

To enable multi-tenancy support, you can use AddMultiTenancy method in IVaultConfigurationSpecification:

builder.AddMultiTenancy(new VaultMultiTenancyOptions<IMultiTenant, int>
{
    TenantIdAccessor = x => x.TenantId,
    TenantIdSetter = (entity, tenantId) => entity.TenantId = tenantId,
    TenantIdProvider = serviceProvider => serviceProvider.GetRequiredService<ITenantProvider>().TenantId
});

Transaction

MongoFlow applies all operations to the database with a single transaction when SaveChangesAsync is called. However, you can use BeginTransaction method to start a transaction explicitly.

using var transaction = vault.BeginTransaction();

try
{
    // Do something
    await transaction.CommitAsync();
}
catch
{
    await transaction.RollbackAsync();
    throw;
}

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.md file for details.

Support

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

NuGet packages (1)

Showing the top 1 NuGet packages that depend on MongoFlow:

Package Downloads
MongoFlow.Identity

A MongoDB provider for ASP.NET Core Identity that leverages MongoFlow for seamless integration.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.3.1 60 11/23/2024
0.3.0 39 11/23/2024
0.2.6 196 11/11/2024
0.2.5 96 11/11/2024
0.2.4 92 11/10/2024
0.2.3 105 11/10/2024
0.2.2 84 11/10/2024
0.2.1 108 11/9/2024
0.2.0 77 11/9/2024
0.1.4 104 11/7/2024
0.1.3 70 11/7/2024
0.1.2 92 11/6/2024
0.1.1 76 11/6/2024
0.1.1-alpha.2 34 11/6/2024
0.1.1-alpha.1 32 11/6/2024
0.1.1-alpha.0 34 11/6/2024
0.1.1-alpha 67 11/6/2024