0

A controller method calls this ValidateUser method. For example to validate user. The service then calls the dbcontext. For example to make the db query.

What scope (transient/scoped/singleton) is suitable for the service class?

As I understand, suppose if scope is set to singleton, then there will be only 1 instance of this class throughout the lifetime of the application. And even though the DbContext by default is scoped, only 1 connection will exist (from this service class's instance) throughout the lifetime.

Suppose I set to transient, then each request for the service class will create a new instance of the class. Since dbcontext is scoped, it will be a 1 instance for 1 user's request lifetime.

Suppose I set to scoped, then each user's request for the service class will create new instance of the class. Since dbcontext is scoped, it will be a 1 instance for 1 user's request lifetime

What are the thread safety concerns?

namespace MyApp.Services
{
    public class ValidateService : IValidateService
    {
    private readonly MyAppContext _context;

        public ValidateService(MyAppContext context)
        {
            _context = context;
        }

        public bool ValidateUser(UserDTO user)
        {
            return await _context.Users.AnyAsync(x => x.username == user.UserName);
        }
    }
}
variable
  • 8,262
  • 9
  • 95
  • 215
  • I don't know, I think it's variab.. Oh – Caius Jard Jan 04 '22 at 18:34
  • Incidentally, this question would probably be better received if the definition of the Service class were included – Caius Jard Jan 04 '22 at 18:39
  • I request that this question be opened again. – variable Jan 05 '22 at 14:20
  • Are you asking "how many instances of scoped X, a thing known to an IoC container and a parameter of Y's constructor, are created if I scope Y as a) transient, b) scoped and make N uses of Y?" or are you asking "why is a DbContext not threadsafe?" – Caius Jard Jan 05 '22 at 14:31
  • I have given my understanding of part 1 - and my questions is the part 2 - that is - why is a DbContext not threadsafe in case of singleton and how does it become thread safe for scoped when the service class is scoped or transient. – variable Jan 05 '22 at 14:35
  • It never becomes threadsafe, it's just harder to arrange a situation where it's being used concurrently if it's scoped. Make a Blazor Server app, or fire multiple ToListAsync without awaiting if you want to get a scoped context to complain – Caius Jard Jan 05 '22 at 14:39
  • Just trying to understand whether by thread safety you mean that multiple threads making use of the same connection concurrently and then it clashes and fails? – variable Jan 05 '22 at 14:40
  • I don't think i mentioned thread safety of a connection at all, and only started talking about it in context terms when you did.. Attempting to use a DbContext for operation Y while it is still carrying out operation X results in an exception with the message "A second operation started on this context before a previous operation completed". If you want to know more about the surrounding circumstances for it, you could check out the [source](https://github.com/dotnet/efcore) or see e.g. https://stackoverflow.com/questions/68603561/multithreaded-dbcontext-operations – Caius Jard Jan 05 '22 at 14:49
  • Ok so what is the example of thread safety other than the connection please? I'm learning here and guidance is very helpful. – variable Jan 05 '22 at 14:50
  • Thread safety with regards to internal data storage (collections) would be my top pick – Caius Jard Jan 05 '22 at 16:32
  • 1) Ok is it good practice to use TransientScope there by there wont be any thread safety issues. 2) I understand that scope and singleton are useful only for sharing state. Is there any other use case of theirs? – variable Jan 05 '22 at 17:49
  • For example - see this send email class is specified as Singleton - is that thread safe - https://medium.com/@ajidejibola/how-to-send-emails-in-net-core-web-api-547243226453 – variable Jan 05 '22 at 18:05
  • Even with transient, you can blow it up. You can blow it up in a single line: `await Task.WhenAll( new[]{ context.Table.ToListAsync(), context.Table.ToListAsync() })`. Nothing about scope gives thread safety; using it with one thread at once gives safety. We don't really do rambling/wandering OT discussions on comments, and question 2 isn't suited to SO in its current form. Even 1) is a departure from the question originally asked – Caius Jard Jan 05 '22 at 18:07
  • @CaiusJard - is this singleton scope of email service class correct or does it need to be scoped? https://github.com/JiboGithub/EmailSender/blob/master/Startup.cs – variable Jan 06 '22 at 11:57

1 Answers1

2

During dependency injection DbContext is initialized into a pool of objects reused with every request therefore having DbContext with a scoped lifetime takes advantage of this. As for the Service class used together with dbcontext, it is adviced to match the shortest lifetime of an existing object (in this case scoped because of dbcontext).

Theres more information in the following links.

https://codereview.stackexchange.com/questions/15703/threadsafe-dbcontext-in-singleton

and

https://stackoverflow.com/questions/37507691/entity-framework-core-service-default-lifetime
Daniel Kiptoon
  • 314
  • 1
  • 7
  • 1
    Additional info... Singleton in the service class would not be thread-safe, and each HTTP request runs on a different thread. You would quickly find your DbContext instance throwing exceptions related to concurrency. – Eric J. Jan 04 '22 at 18:48
  • When I create the `builder.Services.AddDbContext` there is no option to specify scope – variable Jan 04 '22 at 18:52
  • @eric - I'm not using global variable so why is it not thread safe? – variable Jan 04 '22 at 18:54
  • 1
    A singleton is effectively a global variable. Only one instance of the class exists. – Eric J. Jan 04 '22 at 19:34
  • builder.Services.AddDbContext() is an overloaded method with optional parameters, Lifetime is one such optional parameter that defaults to scoped if not supplied. – Daniel Kiptoon Jan 05 '22 at 07:36
  • @EricJ. - I agree that singleton is global, but if the class doesn't have any state then what is the concern? – variable Jan 05 '22 at 14:12
  • If I understood correctly, you inject a DbContext into that singleton variable then store it in a private field. That DbContext instance isn't thread safe. – Eric J. Jan 05 '22 at 17:15
  • Can you update a summary of the above comments into the answer please so it will be helpful for readers in future. – variable Jan 12 '22 at 20:44