nbarbettini / little-aspnetcore-book

The Little ASP.NET Core Book, a friendly introduction to web programming and ASP.NET Core 2.0
http://littleasp.net/book
Creative Commons Attribution 4.0 International
701 stars 190 forks source link

Add scoped Vs Add singleton for database when not using Entity Framework #37

Closed Rud156 closed 6 years ago

Rud156 commented 6 years ago

I did not quite get when to use services.AddScoped or services.AddSingleton while using databases.

In the book it is mentioned that when using Entity Framework services.AddScoped is required because of the way Entity Framework handles requests. But is this the same case when using other databases also like MongoDB or CouchDB??

This Stackoverflow link describes the differences between them: https://stackoverflow.com/questions/38138100/what-is-the-difference-between-services-addtransient-service-addscope-and-servi

But the accepted way is not mentioned. Could you provide some better detail?

nbarbettini commented 6 years ago

Hey @Rud156!

The ASP.NET Core docs cover this, although it's a little buried:

Entity Framework contexts should be added to the services container using the Scoped lifetime... Repositories that will make use of Entity Framework should use the same lifetime.

Here's an answer on StackOverflow that gives better detail: Entity Framework Core service default lifetime

In short, EF uses the Scoped lifetime so that the DbContext gets disposed after every request. This means that each request is treated like a transaction unless you explicitly write your own transaction code. The request-as-transaction behavior is a nice default that works for many applications.

But is this the same case when using other databases also like MongoDB or CouchDB??

Most likely yes, although I think it depends on how you're using Mongo or Couch. Are you using them with EF, or using a package like mongo-csharp-driver to directly connect? If so, you'll need to think about connections, contexts, and transactions yourself. The right answer can depend on how the database driver works.

nbarbettini commented 6 years ago

For example, the Mongo C# driver docs recommend that you create one MongoClient and store it with a singleton-type lifetime. So, very similar. 😄

Rud156 commented 6 years ago

@nbarbettini Thanks a lot for the explanation. Will look into it

nbarbettini commented 6 years ago

Let me know if you have further questions!

redar84 commented 3 years ago

I am having this issue too, Lately, I started working on MongoDB. MongoDB recommends singleton for MongoClient. So I am still not sure about my implementation, and I am confused. I implemented the Mongo in DI container two ways, and I am not sure which one is good. Lets take the first approach

Here I return a singleton instance of IMongoClient

services.AddSingleton<IMongoClient>(_ =>
            {
                //var connectionString = con;

                return new MongoClient(con.ConnectionString);
            });

Then,

services.AddScoped<IMongoDatabase>(s =>
            {
                var client = p.GetRequiredService<IMongoClient>();
                return client.GetDatabase(con.DatabaseName);
            });

Then, return a scoped for my IMongoDatabase. In my repo, I inject the IMongoDatabaseand then call my DB.

 _dataContext = mongoDBClient.GetCollection<SomeCollection>(GetCollectionNameFromAppSetting((settings.DPUBotCollectionName)));

The second one I was returning an IMongoDatabase as singleton:

services.AddSingleton<IMongoDatabase>(_ =>
            {
                //var connectionString = con;

                return new MongoClient(con.ConnectionString).GetDatabase("SomeDatabase");
            });

Monog says their MonogClient and IMongoDatabase are thread-safe. I am not sure which approach is right. I would appreciate it if you could give me an answer.

nbarbettini commented 3 years ago

When the docs say,

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. (...) The implementation of IMongoDatabase provided by a MongoClient is thread-safe and is safe to be stored globally or in an IoC container.

It means that you can use AddSingleton for both of them.

Your 2nd example looks good to me, especially if you only ever have 1 database. Adding IMongoDatabase as a singleton (the way you do in your 2nd example) ensures that the database connection is set up only once for the lifetime of the application, which is exactly in line with the recommendation.