2

Entity framework core 2.0 introduce DbContext Pooling. In my code I do a lot of jobs in Tasks because I do some independent heavy operations on database.

My old approach was:

Task.Run(() =>
{
    AppDbContext c = new AppDbContext(this.config); 

How can I get instance from EF Core 2.0 DbContext Pooling?

Edited:

I am using DI: public CategoryController(AppDbContext context, ... Reason for doing this is quicker execute Rest API method.

For example, I think this should complete quicker

    List<AppUser> users;
    List<DbGroup> groups;
    Task task1 = Task.Run(async() => {
        users = await ContextFromConnectionPool.Users.Where(t => t.Id == 1).ToListAsync();
    });
    Task task2 = Task.Run(async () => {
        groups = await ContextFromConnectionPool.Groups.Where(t => t.Id == 1).ToListAsync();
    });
    var tags = await this.context.Tags.ToListAsync();
    Task.WaitAll(task1, task2);
    //process all 3 results

then this:

    List<AppUser> users = await this.context.Users.Where(t => t.Id == 1).ToListAsync();
    List<DbGroup> groups = await this.context.Groups.Where(t => t.Id == 1).ToListAsync();
    var tags = await this.context.Tags.ToListAsync();
    //process all 3 results

In second example second query executes after first is completed.
If every query takes 150ms in first example method execute in approx 150ms, but second in approx 450ms. Am I right?
Only problem is how to get context from connection pool in first approach.

Makla
  • 9,899
  • 16
  • 72
  • 142
  • 1
    The pooling is designed to be used with ASP.NET Core's Dependency Injection, and is useful there to support a high frequency of short-lived Controller instances. I don't know if it's possible or easy to use otherwise. If you are doing "heavy" operations that aren't tied to web request lifetime, in a task, it's unlikely that your application would benefit from DbContext pooling. You still have SqlConnection pooling. – David Browne - Microsoft Aug 17 '17 at 18:49
  • It is tied to web request. It's just I need to query several independent tables, and I was using Task to do that. – Makla Aug 17 '17 at 19:58
  • There's no need to use Tasks, then. Just use a single Async method in your controller and use the DbContext provided to the controller constructor and 'await' async operations on the DbContext. – David Browne - Microsoft Aug 17 '17 at 20:21
  • But this way I can not run 2 (time consuming) queries at once (simultaneously). – Makla Aug 17 '17 at 20:34
  • Again for long-running queries DbContext pooling is unnecessary. So just create what you need in the controller method body. – David Browne - Microsoft Aug 17 '17 at 21:07

1 Answers1

2

The feature of ASP.NET Core 2.0 and Entity Framework Core 2.0, to support connection pooling, is not — in any way — preventing you from doing the time consuming queries at once. The entire concept of pooling is to allow the connection to be reused in multiple requests, instead of having to recreate an instance each time a new request comes in. Sometimes, it can have benefits and sometimes it might have downfalls. Now, for your question, there are two pathways,

  1. Allow the framework to pool the connection in Startup class and then reuse those objects everywhere you need. You can capture them inside the actions, and any other private or local functions that you have.
  2. Do not use DI and database context pooling and instead keep doing what you were doing. Note that, you were never using DI and thus there is no need to register your database context in the Startup class. But you must take care of creation of instance, manually disposing the instance as well.

Second approach is not suitable, and not a good approach as well, for many reasons. If you want to consider the first approach you can then change your controller to accept a property of the type database context, such as,

public class YourController : Controller {
    public AppDbContext c { get; set; }

    public YourController (AppDbContext c) {
        this.c = c;
    }
}

Now if you have got that, you can then use this c variable inside your tasks, and run the time consuming queries inside that function — which in any way would be too useless. You can do this,

Task.Run(() => 
{
    // Use c here.
});

Just remember a few points:

  1. It is good to build your query, and then call ToListAsync()ToList() may not be suitable, consider using ToListAsync() and apply await keyword for asynchronously capturing the data.
  2. Your query only gets executed on the database server, when you call ToList or any similar function.
  3. While running tasks in parallel, you must also handle any cases where your query might break the policies, such as data integrity or similar cases in database. It is always a best practice to catch the exceptions.

In your case, for just better practicing you might want to consider wrapping your code inside using block,

Task.Run(() => {
    using (var context = new AppDbContext) {
        // use context here.
    }
}

This is the best that I can state to help you, since you have not shared 1) purpose of not using DI, 2) the sample of your query (why not using LINQ to build query and then executing on server?) 3) any sample code to be used. I hope this would give you an idea of, why you should consider using DI and using the instances returned from there.

Afzaal Ahmad Zeeshan
  • 15,669
  • 12
  • 55
  • 103
  • Hi, great answer. I already use most of advice you write. I am using DI in my controllers. Reason is I want to query multiple tables simultaneously. I expect it to be much quicker as query one table and wait for response, and then query another one and wait for response. I edited my post to better explain my question. – Makla Sep 04 '17 at 05:18
  • 1
    @Makla, you are right you would require two separate requests. What I can recommend here is the first approach, of running two queries and then waiting on their `Task` objects. One thing to note here is, that you can have two context objects in both functions, because you are not writing (modifying) data, it is safe to use this approach. – Afzaal Ahmad Zeeshan Sep 04 '17 at 09:44
  • 1
    Here is a similar question and you can dig something from here, https://stackoverflow.com/questions/41749896/ef-6-how-to-correctly-perform-parallel-queries – Afzaal Ahmad Zeeshan Sep 04 '17 at 09:44
  • Do you know if creating new AppDbContext use connection from .NET Core 2.0 DbContextPool? – Makla Sep 04 '17 at 10:47
  • 1
    No, once you create a new instance yourself you will not be using the DbContextPool instance anymore, because that is used for DI. But since DI is not helpful, there is no need to depend on it. When you do `new` on the context object, it does not even use the `AddDbContext` from `Startup`, because your object is not controlled by the framework, and is all managed by you and then you must follow _best practices_ such as the `using` block. – Afzaal Ahmad Zeeshan Sep 04 '17 at 11:51
  • Understand. I hoped I can get connection out of pool, for faster execution (no need to connect to server). – Makla Sep 04 '17 at 13:09
  • @Makla, that depends and pooling does not guarantee performance always. Sometimes a new object might be useful. :-) – Afzaal Ahmad Zeeshan Sep 04 '17 at 13:56