Skip to content

integration mongodb examples

Brian Greco edited this page Apr 9, 2023 · 1 revision

IMAGE MongoDB: Example

This tutorial will show configuring and accessing a MongoDB database. An application infrastructure component such as a repository can access a MongoDB database by injecting the IMongoDbClient service. The IMongoDbClient interface accepts a generic parameter representing the database settings stored in the application's settings.

Configuration

Define a MongoSettings derived application settings class representing the database. Add the following class definition to a Repositories directory within the Examples.MongoDB.Infra project:

using NetFusion.Core.Settings;
using NetFusion.Integration.MongoDB.Plugin.Settings;

namespace Examples.MongoDB.Infra.Repositories;

[ConfigurationSection("netfusion:mongoDB:geographicDB")]
public class GeographicDb : MongoSettings
{

}

The IMongoDbClient is a generic interface that accepts the database configuration for its parameter. The database settings parameter derives from the MongoSettings class containing the common MongoDB connection properties. In turn, the MongoSettings class derives from IAppSettings so all common implementations contained within NetFusion.Settings applies to database settings.

Within the Examples.MongoDB.WebApi project, add the following to the appsettings.json file to specify the database connection:

{
  "netfusion": {
    "mongoDB": {
      "geographicDB": {
        "MongoUrl": "mongodb://localhost:27027",
        "DatabaseName": "geographic"
      }
    }
  }
}

Data Model

Define a class representing the model stored within the database. For this example, the class will be placed in the Examples.MongoDB.Domain project and mapped directly to a MongoDB collection. Add the following class the Entities directory:

namespace Examples.MongoDB.Domain.Entities;

public class StateInfo
{
    public string Id { get; set; } = string.Empty;
    public string State { get; set; } = string.Empty;
    public string Capital { get; set; } = string.Empty;
    public string ZipCode { get; set; } = string.Empty;
}

Next, the above entity needs to be mapped to a MongoDB collection. The mapping class is an infrastructure concern and can be placed in Repositories directory of the Examples.MongoDB.Infa project:

using Demo.Domain.Entities;
using NetFusion.MongoDB;

namespace Demo.Infra.Repositories
{
    public class StateInfoMap : EntityClassMap<StateInfo>
    {
        public StateInfoMap()
        {
            CollectionName = "info.states";
            AutoMap();
            MapStringPropertyToObjectId(m => m.Id);
        }
    }
}

The NetFusion.Integration.MongoDB plug-in will automatically find all mapping classes and register them with the MongoDB driver.

Repository

Add the following repository contract to the Repositories directory of the Examples.MongoDB.App project:

using System.Threading.Tasks;
using Examples.MongoDB.Domain.Entities;

namespace Examples.MongoDB.App.Repositories;

public interface IStateInfoRepository
{
    Task<string> Add(StateInfo stateInfo);
    Task<StateInfo?> Read(string state);
}

Next, inject the IMongoDbClient interface into the repository for the needed database and reference the collection to be updated and queried. Within the Repositories directory of the Examples.MongoDB.Infra project, add the following repository implementation:

using System.Linq;
using System.Threading.Tasks;
using Examples.MongoDB.App.Repositories;
using Examples.MongoDB.Domain.Entities;
using MongoDB.Driver;
using NetFusion.Integration.MongoDB;

namespace Examples.MongoDB.Infra.Repositories;

public class StateInfoRepository : IStateInfoRepository
{
    private IMongoCollection<StateInfo> StateInfoColl { get; }

    public StateInfoRepository(IMongoDbClient<GeographicDb> geographicDb)
    {
        this.StateInfoColl = geographicDb.GetCollection<StateInfo>();
    }

    public async Task<string> Add(StateInfo stateInfo)
    {
        await StateInfoColl.InsertOneAsync(stateInfo);
        return stateInfo.Id;
    }

    public async Task<StateInfo?> Read(string state) 
    {
        var results = await StateInfoColl.Find(i => i.State == state)
            .ToListAsync();

        return results.FirstOrDefault();
    }
}

Since the examples are based on a solution generated using the NetFusion Microservice template, it contains a plug-in module that will register all repositories by convention:

using Microsoft.Extensions.DependencyInjection;
using NetFusion.Core.Bootstrap.Catalog;
using NetFusion.Core.Bootstrap.Plugins;

namespace Examples.MongoDB.Infra.Plugin.Modules;

public class RepositoryModule : PluginModule
{
    public override void ScanForServices(ITypeCatalog catalog)
    {
        catalog.AsImplementedInterface("Repository", ServiceLifetime.Scoped);
    }
}

Consuming Repository

The last step is to inject the repository. This tutorial will inject the IStateInfoRepository interface directly into a controller. Add the following controller to the Examples.MongoDB.WebApi project:

using Examples.MongoDB.App.Repositories;
using Examples.MongoDB.Domain.Entities;
using Microsoft.AspNetCore.Mvc;

namespace Examples.MongoDB.WebApi.Controllers;

[Route("api/[controller]")]
[ApiController]
public class StateInfoController : ControllerBase
{
    private IStateInfoRepository StateInfoRep { get; }

    public StateInfoController(IStateInfoRepository stateInfoRep)
    {
        StateInfoRep = stateInfoRep;
    }

    [HttpPost]
    public Task<string> AddStateInfo([FromBody]StateInfo stateInfo)
    {
        return StateInfoRep.Add(stateInfo);
    }

    [HttpGet("{state}")]
    public async Task<IActionResult> GetStateInfo(string state)
    {
        var entity = await StateInfoRep.Read(state);
        if (entity == null)
        {
            return NotFound();
        }

        return Ok(entity);
    }
}

The IMongoDbClient interface contains the following methods:

public interface IMongoDbClient<out TSettings>
    where TSettings : MongoSettings
{
    IMongoCollection<TDocument> GetCollection<TDocument>(string,                  name,MongoCollectionSettings settings = null);

    IMongoCollection<TEntity> GetCollection<TEntity>     
         (MongoCollectionSettings settings = null) where TEntity : class;

    Task DropCollectionAsync<TEntity>() where TEntity : class;
}

The above methods allow the referencing to MongoDb collections based on entity type without specifying the name of the corresponding collection's name. If the collection name is not explicitly specified, the IMongoDbClient implementation will check the entity's corresponding mapping to see if the collection name is set.

This allows the collection to be referenced simply as:

_customerColl = _refArchDb.GetCollection<StateInfo>();

and not by the following:

_customerColl = _refArchDb.GetCollection<Customer>("info.states");

If the mapping does not provide a collection name, the fully qualified type's name will be used.

Execute Example

Test the above example by posting and retrieving data to the API controller as follows:

cd ./src/Examples.MongoDB.WebApi
dotnet run

IMAGE

IMAGE

IMAGE

Entity Mapping Know-Types

Also, the bootstrap process will call the AddKnownPluginTypes method on map instances before they are registered. This allows plug-in entity mappings to register any types that are derived from the type being mapped. This is not specific to NetFusion and is how the MongoDB driver knows how to deserialize derived types from a common base class. The following is an example:

public class CustomerMapping : EntityClassMap<Customer>
{
    public CustomerMapping()
    {
        this.AutoMap();
        this.MapStringPropertyToObjectId(p => p.CustomerId);
    }

    public override void AddKnownPluginTypes(IEnumerable<Type> pluginTypes)
    {
        var customerTypes = pluginTypes.Where(t =>
            t.IsDerivedFrom<Customer>() && !t.IsAbstract && !t.IsInterface);

        customerTypes.ForEach(s => this.AddKnownType(s));
    }
}
Clone this wiki locally