Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I register my mongodb service that uses the MongoClient, Singleton or scoped?

I am building an API with ASP.NET core using Mongodb and i have different services user service home service and etc. I would like to know should i register every service as singleton as it is mentioned in asp.net core documention or as scoped. Link to repository https://github.com/krisk0su/apartments

UserService.cs

public class UserService
{
    private readonly IMongoCollection<User> _books;
    private readonly IPasswordHasher _passwordService;

    public UserService(IBookstoreDatabaseSettings settings,  IPasswordHasher passwordService)
    {
        var client = new MongoClient(settings.ConnectionString);
        var database = client.GetDatabase(settings.DatabaseName);

        _books = database.GetCollection<User>(settings.UsersCollectionName);
        _passwordService = passwordService;
    }

    public List<User> Get() =>
        _books
        .Find(book => true)
        .ToList();

    public User Get(string id) =>
        _books.Find(user => user.Id == id).FirstOrDefault();

    public User Create(User user)
    {
        var password = this._passwordService.HashPassword(user.Password);
        user.Password = password;
        _books.InsertOne(user);
        return user;
    }
    public void Update(string id, User bookIn) =>
        _books.ReplaceOne(book => book.Id == id, bookIn);

    public void Remove(User bookIn) =>
        _books.DeleteOne(book => book.Id == bookIn.Id);

    public void Remove(string id) =>
        _books.DeleteOne(book => book.Id == id);
}

Startup.cs

services.AddSingleton<UserService>();
            services.AddSingleton<BookService>();
            services.AddSingleton<AuthenticationService>();
            services.AddScoped<IPasswordHasher, PasswordHasher>();
like image 820
Christian Avatar asked Jan 05 '20 10:01

Christian


People also ask

Is MongoClient a singleton?

Typically yes, it is a good practice to make MongoClient a singleton. As mentioned in the MongoDB Java driver: The MongoClient instance represents a pool of connections to the database; you will only need one instance of class MongoClient even with multiple threads.

How does Mongodb connect to MongoClient?

Connect to a Replica Setconst MongoClient = require('mongodb'). MongoClient; const assert = require('assert'); // Connection URL const url = 'mongodb://localhost:27017,localhost:27018/?replicaSet=foo'; // Database Name const dbName = 'myproject'; // Use connect method to connect to the Server MongoClient.

What is Mongodb MongoClient?

The MongoClient class is a class that allows for making Connections to MongoDB. remarks. The programmatically provided options take precedence over the URI options. example // Connect using a MongoClient instance const MongoClient = require('mongodb').


3 Answers

Well its complicated.

First of all MongoClient can be singleton, so all services that uses MongoClient can be singletons as well. Its important cause singleton service cannot depends on service with shorter life (Scoped, Transient).

Now about UserService. All its dependancies is singletons and service itself don't stores any data (no fields, no props) that should live limited time or any data about particular user etc.

So it can be singleton!

But if you decided to add scoped dependancy or store any data in it:

public class UserService
{
    private readonly IMongoCollection<User> users;
    private readonly long userCount; //this one

    public UserService(IBookstoreDatabaseSettings settings)
    {
        var client = new MongoClient(settings.ConnectionString);
        var database = client.GetDatabase(settings.DatabaseName);

        users = database.GetCollection<User>(settings.UsersCollectionName);
        userCount = users.Find(_ => true).CountDocuments();
    }
}

then you have to make it at least Scoped.


Btw it's much easier to have MongoClient as singleton in DI:

services.AddSingleton<IMongoClient>(s => 
    new MongoClient(Configuration.GetConnectionString("MongoDb"))
);

and then use it in all services:

public class UserService
{
    private readonly IMongoCollection<User> users;

    public UserService(IMongoClient mongoClient)
    {
        var database = mongoClient.GetDatabase("DatabaseName");
        users = database.GetCollection<User>(settings.UsersCollectionName);
    }
}

Or if you will use just one database in your app you can move IMongoDatabase to DI as well and then you don’t need to get it every time in the service constructor.

like image 178
Victor Trusov Avatar answered Oct 23 '22 07:10

Victor Trusov


The MongoDB .NET Driver reference documentation for version 2.17 explains on the Reference > Driver > Connecting page in the Mongo Client Re-use section that:

It is recommended to store a MongoClient instance in a global place, either as a static variable or in an IoC container with a singleton lifetime.

With regards to Mongo Database Re-use it doesn't mention a singleton lifetime but it does say it "is thread-safe and is safe to be stored globally", so I would interpret that to mean it can be stored safely as a singleton if that's what your implementation desired, but it doesn't need to be if you prefer another lifetime.

The implementation of IMongoDatabase provided by a MongoClient is thread-safe and is safe to be stored globally or in an IoC container.

It's the same with regards to Mongo Collection Re-use:

The implementation of IMongoCollection<TDocument> ultimately provided by a MongoClient is thread-safe and is safe to be stored globally or in an IoC container.

So again I'd interpret that to mean the choice of lifetime is up to your specific requirements.

It seems it's only the MongoClient that carries a recommendation to use a singleton lifetime.

like image 12
Owain Williams Avatar answered Oct 23 '22 07:10

Owain Williams


Thank you for sharing, I am working on a MongoDB and .net core project. I have one DB with multiple collections. In start-up class services.AddSingleton(s => { return new MongoClient(con.ConnectionString).GetDatabase(con.DatabaseName); });

My connection string and DB info are stored inside my appSetting.json. Now in my repo, I inject

mongoDBClient.GetCollection<SomeClass>(GetCollectionNameFromAppSetting((settings.CollectionName)));

Since I am having one Database would that be ok to have that registered as a singleton? or should I change it

like image 1
redar ismail Avatar answered Oct 23 '22 06:10

redar ismail