Daily Knowledge Drop
When retrieving a single record from the database using Entity Framework, the Single (or SingleOrDefault)
method can be used - however the Find
method on the DbSet is potentially more efficient, however it comes with some limitations.
First
The first method is using First (or FirstOrDefault)
- this is an extension method on Queryable (an important factor when comparing it to the Find method)
As DbSet implement IQueryable, this method can be use directly on the DbSet.
Assuming we have a DbContext with a "Song" DbSet, a single record can be retrieved as follows:
using var context = new DemoContext();
// using Single
var song = context.Songs.Single(x => x.Id == 43);
// using SingleOrDefault
var song2 = context.Songs.SingleOrDefault(x => x.Id == 43);
Find
The next method is using Find
- this is an extension method on DBset itself.
Again, assuming we have a DbContext with a "Song" DbSet, a record can be retrieved as follows using Find:
using var context = new DemoContext();
var song = context.Songs.Find(43);
Limitation
As mentioned above, having Find
as an extension method on DbSet does limit its usage. It cannot be used in conjunction with other IQueryable extension methods.
The following examples are NOT valid and will NOT compile:
// Find() returns a 'Song' and as such Include()
// cannot be used in this example
var song = context.Songs.Find(43).Include("Artist");
// Include() returns a 'IQueryable' and as such Find()
// cannot be used in this example
var song2 = context.Songs.Include("Artist").Find(43);
However, the following using Single
is VALID:
// Include() returns a 'IQueryable' and as Single is
// an extension method on 'IQueryable' it CAN be used
var song = context.Songs.Include("Artist").Single(x => x.Id == 43);
Benchmark
The different methods where benchmarked, with a:
Shared DbContext
- defined in the constructor once, and reusedSingle-use DbContext
- a new DbContext is declared for each database call
Method | Mean | Error | StdDev | Median | Ratio | RatioSD |
---|---|---|---|---|---|---|
FindSharedContext | 14.93 us | 2.845 us | 8.071 us | 12.00 us | 0.14 | 0.07 |
SingleDefaultSharedContext | 123.26 us | 2.294 us | 2.146 us | 123.22 us | 0.98 | 0.05 |
SingleSharedContext | 123.78 us | 2.320 us | 4.242 us | 122.09 us | 1.00 | 0.00 |
FindOneUseContext | 488.81 us | 9.539 us | 10.207 us | 487.06 us | 3.91 | 0.20 |
SingleDefaultOneUseContext | 261.49 us | 5.098 us | 5.236 us | 260.91 us | 2.09 | 0.11 |
SingleOneUseContext | 259.93 us | 5.166 us | 10.317 us | 257.45 us | 2.11 | 0.11 |
The Find method is substantially quicker
when reusing the DbContext (as one should).
However, interestingly, with a single-use DbContext, the Single/SingleOrDefault method is almost twice as quick as the Find method.
Notes
While Find is approximately 7 times
faster than the equivalent Single method, it does come with considerable limitations. If performance is critical, it might be worth finding a way to work around the limitations - however keep in mind that the differences are being measured in microseconds, so the performance gain might not be worth the potential additional effort.
Be aware of the performance differences, and the limitations - and for each specific use case, apply the more appropriate technique.
References
Daily Drop 109: 04-07-2022
At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.
The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.On This Page