Entity Framework - How To Configure One-To-Many Relationship Navigation Properties Correctly

Viewed 89

I have the following code with a basic one-to-many setup. Following the guidelines from entityframeworktutorial.net I'm trying to understand where to place navigation properties. I went with convention 4 as stated in the tutorial with non-nullable key.

public class VendorImage
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int VendorImageId { get; set; }
    [Required, MaxLength(50)]
    public string  ImageUrl { get; set; }

    public int VendorId { get; set; }
    public Vendor Vendor { get; set; }
}

public class Vendor
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int VendorId { get; set; }
    [Required, MaxLength(50)]
    public string Name { get; set; }

    public ICollection<VendorImage> VendorImages { get; set; }
}

Testing it with postman, I get the error "A possible object cycle was detected which is not supported."

I then remove the Vendor property from VendorImage, and it works fine.

Is there an explanation for this? I would like to understand.

1 Answers

Your error is not the result of a mistake in your entity design, but rather that of your json serializer. The json serializer sees a Vendor reference in the VendorImage class, and can't cope with the object nesting.

To resolve this, you can either do this quick hack (this should work for both System.text.Json and NewtonSoft, but make sure to use the correct using statement):

public class VendorImage
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int VendorImageId { get; set; }
    [Required, MaxLength(50)]
    public string  ImageUrl { get; set; }

    public int VendorId { get; set; }
    [JsonIgnore] //Add an attribute to ignore this property during serialization.
    public Vendor Vendor { get; set; }
}

public class Vendor
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int VendorId { get; set; }
    [Required, MaxLength(50)]
    public string Name { get; set; }

    public ICollection<VendorImage> VendorImages { get; set; }
}

or tell your service to ignore loop handling:

services.AddControllers().AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
Related