3

I am using ASP.net core. I have problem with implementing dbcontext into singleton.

I need my singleton IModuleRepository to be running right after start of the project. So I am creating new instance of this dependency in public void ConfigureServices(IServiceCollection services) in Startup.cs file.

This singleton is using another singleton, so I am using it like this:

services.AddDbContext<ModulesDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")).EnableSensitiveDataLogging());
...    
services.AddSingleton<IModuleRepository, ModuleRepository>();
services.AddSingleton<ICommunicationRepository>(new CommunicationRepository(services.BuildServiceProvider().GetService<IModuleRepository>()));

In ModuleRepository I am using DBcontext.

// Db context
    private readonly ModulesDbContext _modulesDbContext;

    public ModuleRepository(ModulesDbContext modulesDbContext)
    {
        _modulesDbContext = modulesDbContext;
    }

When I am calling _modulesDbContext.SomeModel.ToList(); I get error:

System.InvalidOperationException: An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point.

How to avoid this error when I need this singleton to run after the project is started?

Thank you for your help.

2
  • Don't resolve dependencies explicitly. Try services.AddSingleton<ICommunicationRepository, CommunicationRepository>(); instead. Commented May 2, 2017 at 8:48
  • @IlyaChumakov Same answer as for juunas below. Commented May 2, 2017 at 8:59

2 Answers 2

2

As @Ilya Chumakov commented, you could just tell the DI container to use your concrete class like so:

services.AddSingleton<ICommunicationRepository, CommunicationRepository>();

Then any class can depend on ICommunicationRepository and get the concrete repository, which gets the Db context.

Sign up to request clarification or add additional context in comments.

8 Comments

I know I can do this. But what will start this singleton instead of Startup? I need to run constructor, where I am creating new Task which is running in the background and communicate with Modbus protocol periodically. So I need to kick it on the start, not in the Controller.
How about moving the Task kickoff to a method, resolving the communication repository from the service provider and calling the method? Also, you could look into task scheduling libraries to run this kind of things.
You can also resolve dependencies in the Configure method and run the startup task from there. That actually would make way more sense than running it in a constructor.
It makes no change when I move it to the method. It is by principe the same thing as having it in constructor. I do NOT have problem with running Task. I have problem with DBContext.
True.. Maybe you should try the approach of injecting ICommunicationRepository as a function parameter to Configure? I run database migrations from there usually so I can say that the Db context should work at that point.
|
-1

I figured out this problem. This calling of the dependencies were right. The error is that in CommunicationRepository I've created 2 Task and both of them were using the same DbContext - so it was using it multiple times. I had to say task.Wait();

Full code in constructor of the CommunicationRepository after correction:

// Add pernament communication
        var task = new Task(AddPernamentCommunicationAll);
        task.Start();
        task.Wait();

        // Add modules
        var taskModules = new Task(AddModulesToList);
        taskModules.Start();

Thank you for your answers.

8 Comments

Tasks are not threads, just call your code sync. Also, do not run async tasks in Constructor, it's pretty bad. consider using a factory to create + initialize an object instead. Also that being said: EF Core isn't thread safe. You can't use the same context from two threads (means: You can't paralellize the operations), so yo shouldn't use it as singleton (two requests could be accessing it, then you receive an exception!)
I know that Tasks is something different than threads. I think that running async in constructor is totally ok in my case. I am not new to these things. In my case it is OK, because if there were no tasks, my server won't be accessible if something went wrong in this code. Yes, I figgured out, that EF core is not thread safe - whole answer is about this. I think that you are out of the context. I have answered my own question, so why is it -1 if this is working?
David, the way you run the first and second task makes no sense at all. You create a task by passing an action to it. One only does that if one wants to run the AddPernamentCommunicationAll on another thread. This makes no sense and is bad in ASP.NET/ASP.NET Core as you are playing with the threadpool, lowering efficiency of your application since you create a new thread to "release" the request thread, you still use only a single thread. You basically gain nothing from it, except the overhead of switching thread/task contexts.
ASP.NET Core works differently than WPF/Desktop applications, when it comes to threads. See this article (its for ASP.NET but still valid for ASP.NET Core) and especially the "What About the Thread Doing the Asynchronous Work?" section to understand why it's absolutely pointless to start non-IO bound tasks in ASP.NET Core.
Also, async in constructor is VERY BAD. When the constructor is finished you are expected that the class is initialized, but when you run an async task while accessing the class it may result in an invalid operation exception, i.e. if in constructor you start a EF db request and then call a method which also calls a db request you get an exception because you try to execute two operations at the same time in parallel with your context, which doesn't work or access a value which isn't loaded yet. If you need to populate some values form database, consider using a factory to initialize it
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.