Skip to content

OR clause allowing NULL is generated when joining on NullableProperty.Value #27071

@sam-wheat

Description

@sam-wheat

Linq below joins h.ModuleId (long?) to cmc.ModuleId (string). Both IDs can be null.

var query = from h in db.BatteryHistories
join cmc in db.CurrentMConfigurations on new { ModuleId = h.ModuleId.ToString() } equals new { ModuleId = cmc.ModuleId }
join m in db.Cu04tb09Ms on  new { M_ID = cmc.M_Number.ToString(), SvcType = cmc.ServiceType  } equals new { M_ID = m.IdMtr, SvcType = m.CdSvcTyp } 

Generated SQL:

SELECT g.*
FROM [BatteryHistory] AS [g]
INNER JOIN [CurrentMConfiguration] AS [c] ON (CONVERT(VARCHAR(20), [g].[ModuleId]) = [c].[ModuleId]) OR ([g].[ModuleId] IS NULL AND [c].[ModuleId] IS NULL)
INNER JOIN [CU04TB09_M] AS [c0] ON (CONVERT(VARCHAR(100), [c].[M_Number]) = [c0].[ID_MTR]) AND ([c].[ServiceType] = [c0].[CD_SVC_TYP])

Linq below joins h.ModuleId.Value (long) to cmc.ModuleId (string). Linq does not allow nulls since h.ModuleId.Value cannot be null. However generated SQL will still select nulls: "OR ([g].[ModuleId] IS NULL AND [c].[ModuleId] IS NULL)"

var query = from h in db.BatteryHistories
join cmc in db.CurrentMConfigurations on new { ModuleId = h.ModuleId.Value.ToString() } equals new { ModuleId = cmc.ModuleId }
join m in db.Cu04tb09Ms on  new { M_ID = cmc.M_Number.ToString(), SvcType = cmc.ServiceType  } equals new { M_ID = m.IdMtr, SvcType = m.CdSvcTyp } 

Generated SQL:

SELECT g.*
FROM [BatteryHistory] AS [g]
INNER JOIN [CurrentMConfiguration] AS [c] ON (CONVERT(VARCHAR(20), [g].[ModuleId]) = [c].[ModuleId]) OR ([g].[ModuleId] IS NULL AND [c].[ModuleId] IS NULL)
INNER JOIN [CU04TB09_M] AS [c0] ON (CONVERT(VARCHAR(100), [c].[M_Number]) = [c0].[ID_MTR]) AND ([c].[ServiceType] = [c0].[CD_SVC_TYP])

Noteably, no null handling is generated for the following Linq. Because cmc.M_Number is not nullable? This seems to be inconsistent with the above. If consistent, EF would handle NULL condition where nullable fields are compared:

join m in db.Cu04tb09Ms on  new { M_ID = cmc.M_Number.ToString(), SvcType = cmc.ServiceType  } equals new { M_ID = m.IdMtr, SvcType = m.CdSvcTyp }

Generated SQL:

INNER JOIN [CU04TB09_M] AS [c0] ON (CONVERT(VARCHAR(100), [c].[M_Number]) = [c0].[ID_MTR]) AND ([c].[ServiceType] = [c0].[CD_SVC_TYP])

Classes (all strings are nullable):

public partial class BatteryHistory
{
	public long? ModuleId { get; set; }
}


public partial class CurrentMConfiguration
{
	public decimal M_Number { get; set; }
	public string ModuleId { get; set; }
	public string ServiceType { get; set; }
}

public partial class Cu04tb09M
{
    public string IdMtr { get; set; }
	public string IdCommMod { get; set; }
	public string CdSvcTyp { get; set; }
}

Versions:
dotnet 5.0
EF 5.0.13

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions