I recently determined that there are no significant performance gains from using a Dependency Injected DbContext in .NET Core and using async await calls as opposed to creating a new DbContext every time I want to access the DB.
But now I need to know why.
I did a much more granular test with System.Diagnostics.Stopwatch in my .NET Core 1.1 API services (which the controller is calling) in which I ran the stopwatch only when accessing the DB. The results were surprising.
When using the standard Dependency Injected context and async/await calls:
var task1 = _InjectedDbContext.Table1.FirstOrDefaultAsync(p => p.Id == SomeId);
var task2 = _InjectedDbContext.Table2.Where(u => u.AnotherId == SomeOtherId).ToListAsync();
(var result1, var result2) = await (task1, task2).WhenAll();
each DbContext query took significantly less than 100 ms.
However, when using this method:
using (var context = new DbContext(_InjectedContextOptions.Options))
{
var task1 = context.Table1.FirstOrDefaultAsync(p => p.Id == SomeId);
var task2 = context.Table2.Where(u => u.AnotherId == SomeOtherId).ToListAsync();
(var result1, var result2) = await (task1, task2).WhenAll();
}
Each DbContext query took anywhere from 100-230 ms.
FYI, here is my code for the DI setup in Startup.cs ConfigureServices:
var connection = Configuration.GetConnectionString("mydb");
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connection));
And here is my code for providing the DbContextOptions as a singleton whenever I create a new DbContext:
var dbContextOptions = new DbContextOptionsBuilder<MyDbContext>();
dbContextOptions.UseSqlServer(Configuration.GetConnectionString("MyDb"));
services.AddSingleton(dbContextOptions);
I also determined that the lag is not caused by simply the creation of the DbContext in the using statement (which is a very fast operation). What is going on here? Is it trying to re-connect to the DB every time or something?
You're using the AddDbContext method, for the DbContext you use in the first scenario, which adds the DbContext to services as Scoped (if I'm not mistaking). The DbContext is probably initialized as soon as the service is added because of optimization (not sure here). For the second case you're creating a new DbContext. Apart from the DbContext creation there's also some other stuff that needs to be done.
Taken from this post, here are some tips to 'warm-up' your context:
Using a cached DB model store
Generate pre-compiled views
Generate pre-compiled version of entityframework using n-gen to avoid jitting
The tips above indicate there's more to using a DbContext than 'just' newing one and start querying.
Related
I am making a data extraction tool (.NET Core console app) that will process tens of thousands of files and write extracted data to a database. There are only insertions to the DB, no reads or updates. I intend to run multiple instances, all writing to the same table.
I am using Autofac for DI, and injecting the EF DB Context into a class that loops through the files and writes to the DB.
I want to keep the DB Context lifetime short, but am not sure how to do that with DI. Is there some kind of "refresh" operation to effectively renew the DBContext? Or should I inject a DBContextFactory and get a new DBContext for each file processed? (And if so, what does the factory look like? Does it explicitly construct a new DBContext?)
If you're holding on to the injected instance of DbContext and using it in a loop, then clearly the lifetime of DbContext cannot be short.
As you suggest, use a Context Builder in the DI. Here's an example that creates a context of type T using SQLServer:
public class DbContextBuilder<T> where T : DbContext
{
public readonly T Context;
public DbContextBuilder(string connectionStringName)
{
IConfigurationBuilder cfgBuilder = new ConfigurationBuilder();
cfgBuilder.AddJsonFile("appsettings.json");
IConfiguration cfg = cfgBuilder.Build();
DbContextOptionsBuilder<T> optsBuilders = new DbContextOptionsBuilder<T>();
optsBuilders.UseSqlServer(cfg.GetConnectionString(connectionStringName));
Context = (T)Activator.CreateInstance(typeof(T), optsBuilders.Options);
}
}
and in each loop:
foreach (var file in files)
{
using (DbContext ctx = new DbContextBuilder<DbContext>("{name of conn string in appsettings}").Context)
{
// Do you writes for the current file
}
}
Where does a custom sql statement go in a code first entity framework project?
using (var context = new BloggingContext())
{
var blogs = context.Blogs.SqlQuery("SELECT * FROM dbo.Blogs").ToList();
}
It doesn't feel right placing it in a controller. So maybe in the IdentityModel.cs class?
One of the strong positives of entity framework is that you seldom need to use SQL strings anymore. The disadvantage of the strings is that they can't be checked by the compiler and thus are error prone, especially after changing the database.
If you'd designed your classes according to the Entity Framework Code First conventions your statement would be like:
using (var dbContext = new BloggingContext)
{
// get all Blogs (note: very unusual!)
var blogs = dbContext.Blogs.ToList();
}
Sometimes you need to explicitly use a SQL statement, for instance when calling a stored procedure. you could use object DbContext.Database:
using (var dbContext = new BloggingContext)
{
string sqlCommand = ...
Database database = dbContext.Database;
database.ExecuteSqlCommand(sqlCommand, <parameters>);
}
Database.ExecuteSqlCommand uses the traditional method of querying databases.
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.
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,
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.
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:
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.
Your query only gets executed on the database server, when you call ToList or any similar function.
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.
I would like to inject my dbcontext into my services in web api 2 project. But I’m a little unsure of how to do that. I've read through the docs but lifecycles, scopes, requests, nested containers...they all leave me a little unsure. I have pieced together the following code from my best understanding. After installing the Nuget package StructureMap.WebApi2, I proceeded to update SM from v3 to v4, as well as the SM.MVC5 from v3 to v4.
The following code appears to have the desired effect of using one dbcontext per http request (I think that's what i want...i'm so confused about scopes) so that the same dbcontext is shared for all services during a users transaction, and then disposed when the transaction (http request) is closed. But appearances are not always right.
IoC.cs
public static IContainer Initialize()
{
var container = new Container(_ =>
{
_.AddRegistry<DefaultRegistry>();
_.ForConcreteType<MyDbContext>().Configure.ContainerScoped();
});
DomainEvents.Initialize(container);
return container;
}}
DefaultRegistry.cs:
public DefaultRegistry()
{
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new Common.Map.MapProfile());
cfg.CreateMissingTypeMaps = true;
});
var mapper = mapperConfig.CreateMapper();
For<IMapper>().Use(() => mapperConfig.CreateMapper());
For<MyDbContext>().Use(()=> new MyDbContext());
Scan(
scan =>
{
scan.AssemblyContainingType<ServiceBase>();
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.ConnectImplementationsToTypesClosing(typeof(IHandle<>));
});
}
The Nuget package StructureMap.WebApi2 created some other files for wiring up resolution and nested containers, but i'm leaving them out of this question for the sake of brevity.
Have a look at the Mehdime DbContext Scope Factory / Locator: https://github.com/mehdime/DbContextScope and he has a bit of a run-down of it on his blog. (link in the project description.) There are forks off there updated to the latest EF versions.
I've found it to be an excellent unit of work wrapper for EF contexts without having to worry about injecting or passing around those contexts directly.
The trouble with IoC containers and constructor injection is that if you register your DBContext it means that it's hard to work with things like a unit of work within the scope of a request without it essentially being the entire scope of the request. The DbContext Scope solves this by having you register a Scope Factory and a Scope Locator in your Container. Code controlling the Scope (unit of work) will use the Scope Factory to initialize a context scope for your DbContexts. Code operating with DbSets etc. in a DbContext use the ContextScopeLocator to get their UoW /w it's DbContext & sets.
I'm not sure if I should work directly with the EntityObject, or if I should make som wrapper methods for selecting, updating and deleting EntityObjects? I would like to use the last option, but I can't really figure out how to return an EntityObject (so I still can call context.SaveChanges();) and not for example a List<Worker> (which is stupid since I can't make changes to the Workers in the list and then easily submit the chages to the database.
For example, I have a Worker table. I could get a worker by using my context:
var worker = context.Worker.Where(w => w.WorkerID == 1).FirstOrDefault();
Or, I could create a wrapper method:
public static RETURNTYPE GetWorkerByID(int id, context)
{
var worker = context.Worker.Where(w => w.WorkerID == 1).FirstOrDefault();
return worker;
}
What kind of returntype should I work with, if I still wish to work with the context (allowing me to alter the data)?
Thanks!
If you make a wrapper type you will not be able to use it with context anymore. Using wrapper means that your return type will represent a new instance filled from the entity object and when you push that instance back you will again need to create / fill entity object to work with.
Have you considered using POCOs (EF 4.0 and newer only)? That would solve your problem because there is on EntityObject base class.