Using EF Core (or any ORM for that matter) I want to keep track of the number of queries the ORM makes to the database during some operation in my software.
I've used SQLAlchemy under Python earlier, and on that stack this is faily easy to set up. I typically have unit tests that assert on the number of queries made for a scenario, against an in-memory SQLite database.
Now I want to do the same thing using EF Core, and have looked at the Logging documentation.
In my test setup code I do as the documentation says:
using (var db = new BloggingContext())
{
var serviceProvider = db.GetInfrastructure<IServiceProvider>();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
loggerFactory.AddProvider(new MyLoggerProvider());
}
But I run into problems that I suspect are the results of the following (also from the docs):
You only need to register the logger with a single context instance. Once you have registered it, it will be used for all other instances of the context in the same AppDomain.
The problems I see in my tests indicates that my logger implementation is shared across multiple contexts (this is in accordance with the docs as I read them). And since a) my test runner runs tests in parallell and b) my entire test suite creates hundreds of db contexts - it does not work very well.
Question/issues:
- Is what I want possible?
- I.e. can I register a logger with a db context that is only used for that db context instance?
- Are there other ways to accomplish what I am trying to do?
Read this: docs.microsoft.com/en-us/ef/core/miscellaneous/logging
If you want to log to static destionation (e.g. console) Ilja's answer works, but if you want to log first to custom buffers, when each dbContext collects log messages to its own buffer (and that what you would like to do in multiuser service), then UPSSS - memory leaks (and memory leak is about 20 mb per almost empty model)...
When EF6 had simple solution to subscribe to an Log event in one line, now to inject your logging this way:
you should write the pooling monster.
P.S. Somebody tell to Ef Core architects that they have wrong understanding of DI and those fancy service locators that they call "containers" and fluent UseXXX that they borrow from ASP.Core can't replace "vulgar" DI from constructor. At least log function should be normally injectable.
*P.P.S. Read also this https://github.com/aspnet/EntityFrameworkCore/issues/10420 . This means that adding LoggerFactory brokes access to InMemory data provider. This is an Abstraction Leak as it is. EF Core has problems with architecture.
ILoggerFactory pooling code:
Call
DbContextOptionsBuilder.UseLoggerFactory(loggerFactory)
to log all SQL output of a particular context instance. You could inject a logger factory in the context's constructor.Usage example:
Generally I use this feature for manual testing. To keep an original context class clean, a derived testable context is declared with overridden
OnConfiguring
method:It's enough to log SQL queries (attach loggers to
loggerFactory
before you pass it to context).Part II: Pass logs to xUnit output and ReSharper test output window
We can create a
loggerFactory
in test class constructor:Test class is derived from
BaseTest
which allows writing toxUnit
output:Most tricky part is to implement a logging provider accepting
IWriter
as a parameter:Required packages:
You can use a bounded context. I used EF Coed first to create two different contexts
Customer bounded context will not log any queries
API bounded context will log the queries
this will log the query to debug output window in VS