I have an entity and context defined in Asp.Net Core Web API project with NRT (Nullable Reference Type) enabled as follows.
public class Inspection
{
public int Id { get; set; }
[StringLength(20)]
public string Status { get; set; } = string.Empty;
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> opts) : base(opts) { }
public DbSet<Inspection> Inspections { get; set; } = null!;
}
Visual Studio Community generates a controller and one of its endpoints is as follows.
[HttpGet]
public async Task<ActionResult<IEnumerable<Inspection>>> GetInspections()
{
if (context.Inspections == null)
{
return NotFound();
}
return await _context.Inspections.ToListAsync();
}
However, many tutorials I read and watch don't check context.Inspections
for null
.
In addition, I have read that ToListAsync
will return an empty list if no entry found.
Question: Is it necessary to do such a check? Does EF Core guarantee that the properties of type DbSet
will never be null
?
CodePudding user response:
No, these properties are initialized by the DbContext constructor.
The constructor uses an IDbSetInitialzer object to initialize all DbSet properties.
ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: false)
.GetRequiredService<IDbSetInitializer>()
.InitializeSets(this);
Using an IDbSetInitializer
like this allows replacing it with mock initializers for test purposes.
The initializer will find all DbSets on the DbContext and initialize them:
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual void InitializeSets(DbContext context)
{
foreach (var setInfo in _setFinder.FindSets(context.GetType()).Where(p => p.Setter != null))
{
setInfo.Setter!.SetClrValue(
context,
((IDbSetCache)context).GetOrAddSet(_setSource, setInfo.Type));
}
}
Before EF Core 6, only entity sets specified through DbSet<> properties were cached, which resulted in performance gains over direct calls to Set<T>()