sadq3377
sadq3377

Reputation: 827

Entity Framework Core: Includes Breaks Http Response

I'm running into a problem setting up a simple many-to-many relationship in Entity Framework Core and Asp .NET Core and it's driving me crazy. Whenever I try to return a related collection with the Includes() method, my HTTP response breaks. Could anybody help me out?

Quick demonstration, here's a simple relationship between Product and Category classes:

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public ICollection<ProductCategory> ProductCategories { get; set; }
}

public class Category
{
    public int CategoryId { get; set; }
    public string Name { get; set; }
    public ICollection<ProductCategory> ProductCategories { get; set; }
}

public class ProductCategory
{
    public int ProductId { get; set; }
    public int CategoryId { get; set; }
    public Product Product { get; set; }
    public Category Category { get; set; }
}

And the Entity Framework DbContext:

public class ProductDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<ProductCategory> ProductCategories { get; set; }

    public ProductDbContext(DbContextOptions<ProductDbContext> options) : base(options)
    {

    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ProductCategory>().HasKey(s => new { s.ProductId, s.CategoryId });

        base.OnModelCreating(modelBuilder);
    }
}

And a controller with a Set method (just a fast way of seeding data) and a Get method to actually get the results:

public class ProductController : Controller
{
    ProductDbContext _context;

    public ProductController(ProductDbContext context)
    {
        _context = context;
    }

    public IActionResult Set()
    {
        _context.Products.AddRange(
            new Product
            {
                Name = "TestProduct1"
            },
            new Product
            {
                Name = "TestProduct2"
            }
            );

        _context.Categories.AddRange(
            new Category
            {
                Name = "TestCategory1"
            },
            new Category
            {
                Name = "TestCategory2"
            }
            );

        _context.SaveChanges();

        _context.ProductCategories.AddRange(
            new ProductCategory
            {
                ProductId = 1,
                CategoryId = 1
            },
            new ProductCategory
            {
                ProductId = 2,
                CategoryId = 1
            },
            new ProductCategory
            {
                ProductId = 2,
                CategoryId = 2
            }
            );

        _context.SaveChanges();

        return Ok();
    }

    public IActionResult Get()
    {
        var products = _context.Products
            //.Include(p => p.ProductCategories)
            .ToList();
        return Ok(products);
    }
}

To test, first I hit the Set method through Postman (http://localhost:54551/product/set). After this, hitting the Get method (http://localhost:54551/product/get) returns this:

[
    {
        "productId": 1,
        "name": "TestProduct1",
        "productCategories": null
    },
    {
        "productId": 2,
        "name": "TestProduct2",
        "productCategories": null
    }
]

However, uncommenting the call to Includes in the Get method gives me this:

Could not get any response

There was an error connecting to http://localhost:54551/product/get.
Why this might have happened:
The server couldn't send a response:
Ensure that the backend is working properly
Self-signed SSL certificates are being blocked:
Fix this by turning off 'SSL certificate verification' in Settings > General
Proxy configured incorrectly
Ensure that proxy is configured correctly in Settings > Proxy
Request timeout:
Change request timeout in Settings > General

What's the piece that I'm missing? I have CORS set up to allow any origin. If I set a breakpoint on return Ok(products) with the Includes() call uncommented, I can see that the data is being added to the products object.

Why would the response fail?

Upvotes: 3

Views: 929

Answers (1)

Cristian Szpisjak
Cristian Szpisjak

Reputation: 2479

This is a circular reference problem for the json serializer. You can fix that by adding in the Startup.cs file the following config:

services.AddMvc()
    .AddJsonOptions(option =>
    {
        option.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    });

Upvotes: 9

Related Questions