3

I have an Asp.net 5/Core 1 application with EF7. I register the DbContext in the DI container normally.

services.AddEntityFramework().AddSqlServer()
  .AddDbContext<MyDbContext>(options => options.UseSqlServer(connection));

Here is a small sample showing what I'm trying to do.

public class MyController : Controller
{
    MyDbContext _myDbContext;

    public MyController(MyDbContext myDbContext)
    {
        _myDbContext = myDbContext;
    }

    public IActionResult Index()
    {
        //Just start these and don't wait for them to finish
        //We don't care if they succeed or not
        Task.Run(() => DoSomeLogging1());
        Task.Run(() => DoSomeLogging2());

        return View();
    }

    private void DoSomeLogging1()
    {
        _myDbContext.Items.ToList();
    }

    private void DoSomeLogging2()
    {
        _myDbContext.Items.ToList();
    }
}

Both DoSomeLoggingX methods will use the MyDbContext instance that was injected to the controller. Since the methods are run at the same time doing db queries at the same time, the other one will invariably fail with

The connection was not closed. The connection's current state is connecting.

MyDbContext also uses DI to get some references so I can't use it directly even if I wanted.

How can I run code in parallel and still be able to use my database through entity framework?

5
  • can you show the code of your entire action ? It seems that you are not waiting for the task to finish. Commented Apr 14, 2016 at 0:22
  • 3
    First thing first, don't spawn background threads in ASP.NET this a very bad practice. For ASP.NET applications, threads are the most important resource you have. If you do async operations you should use await/async, this frees the thread until the async operation is done. If you have CPU sensitive operations do them on the Request thread, don't start a new one for it. DbContexts in ASP.NET are registered as scoped by default, this means they will resolve and life for the duration of the request. When the request ends, they will be disposed. Commented Apr 14, 2016 at 7:10
  • 1
    If you really want to do some logging and immediately return, you need to use some kind of message bus, where you fire log events and they get queued in the message bus and processed by some service which has a dbcontext that has longer lifetime (i.e. app lifetime duration) and do your log processing there or if you use distributed architecture (rabbitmq or other AMQP system) and have a separate process get the messages from the queue and process them Commented Apr 14, 2016 at 7:13
  • 1
    So please clarify your intents on why do you need this kind of thing. I am pretty sure you intent is/was to "free" the request so you don't "block" the request for other users, but what you reached is exactly the opposite: Now you use 2 threads rather than 0 during the async operation Commented Apr 14, 2016 at 7:15
  • The above view is a search form (the action for the submit is omitted in the example). The search searches some in-memory data that is aleady populated. The above DoSomeLogging methods should be named UpdateInMemoryDataIfNeeded. So they check if persisted data is fresh enough, if not fetches new data from an external source and when done replaces the inmemory data with the new data. Commented Apr 14, 2016 at 11:28

3 Answers 3

2

Implement DBContextFactory and inject it in the constructor. Then create new context for every thread.

Fore reference check here Using a DbContext factory (e.g. for Blazor)

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

Comments

1

I also faced with this problem and for now I use my temporary solution EF 7 (Core). Create DBContext like AddTransient

If someone can provide more elegant solution I will be appreciated. Sometimes I really need to return instant result to user and in such cases await/async does not suit

Comments

0

Simple answer: you can't. EF is not made for parallel sql execution (and never will be, I guess).

Less-simple answer. Probably this is not the right way to approach your problem (@Tseng made a point in his comments), but if you need to do a logging stuff like that you can:

  • make the two logging action sequential and share the same EF instance
  • use plain old SQL with a SqlConnection object (which works perfectly with parallel processing) - and it's faster
  • use a logging framework like NLog which supports log-and-forget approach

Comments

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.