Reputation: 605
What is a correct way to select a property of optional navigation property entity framework?
I am concerned that in case the navigation property will be null, then the error will be thrown when I try to access its (optional navigation property`s) property.
Here is what I tried:
return await this.relatedCasesRepository
.GetAll()
.AsNoTracking()
.Where(rc => rc.FirstCaseId == caseId || rc.SecondCaseId == caseId)
.Select(rc => new RelatedCaseInfoDto
{
FirstCaseId = rc.FirstCaseId,
FirstCaseName = rc.FirstCase.Name,
SecondCaseId = rc.SecondCaseId,
SecondCaseName = rc.SecondCase.Name,
CaseRelationTypeId = rc.CaseRelationTypeId,
CaseRelationTypeName = rc.CasesRelationType?.Name,
Id = rc.Id
})
.ToArrayAsync();
The code: rc.CasesRelationType?.Name
produces an error:
An expression tree lambda may not contain a null propagation operator.
Does that mean that I should perform a second request to get all the properties of the optional navigation property? Or is there a way to query the optional navigation property`s property in case the optional navigation property is not null and return null otherwise?
Upvotes: 0
Views: 430
Reputation: 179
The produced SQL will use a LEFT JOIN and thus you can simply omit the null-conditional parameter and write
CaseRelationTypeName = rc.CasesRelationType.Name,
or, with nullable context enabled
CaseRelationTypeName = rc.CasesRelationType!.Name,
This will simply set CaseRelationTypeName
to null
if no entry is found in the linked table.
Attention: With nullable context enabled, this will keep a non-nullable type (e.g. string) for Name
and you would need to cast to string?
manually, making this a bit ugly (this is not necessary for the assignment to work, but would again produce a nullable warning if the target variable is of non-nullable string
):
CaseRelationTypeName = (string?)rc.CasesRelationType!.Name,
This is still shorter than the full conditional, but effectively boils down the choice to a matter of taste:
CaseRelationTypeName = (rc.CasesRelationType != null)
? rc.CasesRelationType.Name
: null;
With nullable context enabled, the latter will handle nullable types intrinsically correct.
With nullable context disabled, I do not see any benefit in taking the long version.
With EF Core 8 for an equivalent example, all options above (including the explicit condition) produce the exact same SQL statement, which essentially just uses a SELECT for the Name
column and a LEFT JOIN for the CasesRelationType
table.
For EF Core, the behavior is described in the documentation here, although in a different context:
When dealing with optional relationships, it's possible to encounter compiler warnings where an actual null reference exception would be impossible. When translating and executing your LINQ queries, EF Core guarantees that if an optional related entity does not exist, any navigation to it will simply be ignored, rather than throwing. However, the compiler is unaware of this EF Core guarantee, and produces warnings as if the LINQ query were executed in memory, with LINQ to Objects. As a result, it is necessary to use the null-forgiving operator (!) to inform the compiler that an actual null value isn't possible: [...]
Upvotes: 0
Reputation: 30454
Why not use a conditional operator?
CaseRelationTypeName = (rc.CasesRelationType != null) ? rc.CasesRealtionType.Name : null;
Upvotes: 2