15

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>();
Liam
  • 27,717
  • 28
  • 128
  • 190
Christian
  • 151
  • 1
  • 1
  • 4
  • `service as singleton as it is mentioned in asp.net core documention` where did you see that? Short answer no, especially if you have db transactions. – panoskarajohn Jan 05 '20 at 11:50
  • https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mongo-app?view=aspnetcore-3.1&tabs=visual-studio . Here they register bookservice as singleton... – Christian Jan 05 '20 at 12:06
  • Does this answer your question? [Why should I not make my services singletons (ioc)?](https://stackoverflow.com/questions/7123201/why-should-i-not-make-my-services-singletons-ioc) – panoskarajohn Jan 05 '20 at 12:18

3 Answers3

19

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.

Owain Williams
  • 2,387
  • 17
  • 22
13

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.

Victor Trusov
  • 1,057
  • 9
  • 19
  • Scoped is quite useful if you're handling multi-doc transations and dont want a direct dependecy in IClientSessionHandle. – sharpc Aug 08 '22 at 12:39
1

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

redar ismail
  • 163
  • 2
  • 13
  • If you have a new question, please ask it by clicking the [Ask Question](https://stackoverflow.com/questions/ask) button. Include a link to this question if it helps provide context. - [From Review](/review/late-answers/30001374) – Andrew H Oct 05 '21 at 23:41