Search code examples
c#asp.net-coreodata

Nullable foreign key causes runtime error in OData service


I'm using OData in an ASP.NET Core Web API.

I have the following entities:

public class Equipment
{
    [Key]
    public long EquipmentId { get; set; }
    [Required]
    public string EquipmentCode { get; set; }
    [Required]
    public string EquipmentTitle { get; set; }
    [Required]
    public bool IsActive { get; set; }
    public DateTime CreationDate { get; set; }

    //Navigation properties
    [ForeignKey("CostCenterRefNavigation")]
    public int CostCenterRef { get; set; }
    public CostCenter CostCenterRefNavigation { get; set; }
    [ForeignKey("EquipmentCatalogueRefNavigation")]
    public int? EquipmentCatalogueRef { get; set; }
    public EquipmentCatalogue EquipmentCatalogueRefNavigation { get; set; }
    [ForeignKey("EquipmentInfoRefNavigation")]
    public int? EquipmentInfoRef { get; set; }
    public EquipmentInfo EquipmentInfoRefNavigation { get; set; }
    [ForeignKey("EquipmentCategoryRefNavigation")]
    public int? EquipmentCategoryRef { get; set; }
    public EquipmentCategory EquipmentCategoryRefNavigation { get; set; }
    [ForeignKey("equipmentTypeRefNavigation")]
    public int EquipmentTypeRef { get; set; }
    public EquipmentType equipmentTypeRefNavigation { get; set; }
    [ForeignKey("BOMRefNavigation")]
    public long? BomRef { get; set; }
    public BOM? BOMRefNavigation { get; set; }
    [JsonIgnore]
    public virtual List<ServiceEquipment> ServiceEquipmentRefNavigation { get; set; }
    [JsonIgnore]
    public virtual List<WorkOrder>? WorkOrdersRefNavigation { get; set; }
    [JsonIgnore]
    public virtual List<WorkOrderDocument>? WorkOrderDocumentsRefNavigation { get; set; }
    [JsonIgnore]
    public virtual List<EquipmentFailure> EquipmentFailuresRefNavigation { get; set; }
    [JsonIgnore]
    public virtual List<EquipmentFailureCause> EquipmentFailureCausesRefNavigation { get; set; }
    [JsonIgnore]
    public virtual List<FailureReport> FailureReportsEquipmentRefNavigation { get; set; }
}

public class EquipmentCategory
{
    [Key]
    public int EquipmentCategoryId { get; set; }
    [Required]
    public string CategoryCode { get; set; }
    [Required]
    public string CategoryName { get; set; }

    //Navigation property
    [JsonIgnore]
    public virtual List<Equipment> EquipmentsRefNavigation { get; set; }
}

public class EquipmentCatalogue
{
    [Key]
    public int EquipmentCatalogueId { get; set; }
    [Required]
    public string RefFileName { get; set; }

    //Navigation property
    [JsonIgnore]
    public virtual List<Equipment>? EquipmentsRefNavigation { get; set; }
}

public class EquipmentInfo
{
    [Key]
    public int EquipmentInfoId { get; set; }
    [Required]
    public string RefFileName { get; set; }

    //Navigation Property
    [JsonIgnore]
    public virtual List<Equipment>? EquipmentsRefNavigation { get; set; }
}

public class EquipmentType
{
    [Key]
    public int EquipmentTypeId { get; set; }
    [Required]
    public string EquipmentTypeTitle { get; set; }

    //Navigation property
    [JsonIgnore]
    public virtual List<Equipment>? EquipmentsRefNavigation { get; set; }
}

This is my program.cs file:

    static IEdmModel GetEdmModel()
    {
        ODataConventionModelBuilder builder = new();
        .
        .
        builder.EntitySet<Equipment>("Equipments");
        .
        .
        return builder.GetEdmModel();
    }
.
.
builder.Services.AddControllers().AddOData(opt => opt.AddRouteComponents("odata", GetEdmModel()).Select().Filter().OrderBy().Count().Expand().SetMaxTop(null));

When I call the target endpoint using the http://localhost:5000/odata/Equipments query, I get an exception:

Microsoft.OData.ODataException: The property 'EquipmentCatalogueRef[Nullable=False]' of type 'Edm.Int32' has a null value, which is not allowed.

How can I fix this error?

Update #1:

OData controller for Equipment entity:

    [HttpGet]
    [EnableQuery]
    [Authorize(Roles = "System,Administrator")]
    public IQueryable<Equipment> Get()
    {
        IQueryable<Equipment> eqList = _sqlServerContext.Equipments.AsQueryable();

        return eqList;
    }

Update #2: I have provided a simple project here. You can see the problem by url query http://localhost:5147/odata/user?$expand=FileRefNavigation.


Solution

  • One thing you should try is to make the key property and navigation one consistent i.e. either both nullable or both non-nullable:

    [ForeignKey("EquipmentCatalogueRefNavigation")]
    public int? EquipmentCatalogueRef { get; set; }
    public EquipmentCatalogue? EquipmentCatalogueRefNavigation { get; set; }