EF Core与ASP.NET无法将子对象插入数组的技术咨询
Got it, let's break down why your EF Core code isn't inserting child objects into the parent entity's list like EF 6 did. The key differences lie in how EF Core tracks entities and validates relationships. Here's a practical solution tailored to your scenario:
1. First, Ensure Your Entity Relationships Are Configured Correctly
EF Core relies heavily on explicit relationship configuration (either via Data Annotations or Fluent API) to properly handle parent-child associations. Let's confirm your entities are set up right:
Example Entity Classes
public class Restaurant { public int Id { get; set; } // Other restaurant properties public ICollection<Menu> Menus { get; set; } = new List<Menu>(); // Initialize to avoid null reference } public class Menu { public int Id { get; set; } // Other menu properties public int RestaurantId { get; set; } // Foreign key to Restaurant public Restaurant Restaurant { get; set; } // Navigation property back to parent }
Fluent API Configuration (Recommended)
Add this to your DbContext's OnModelCreating method to explicitly define the relationship:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Restaurant>() .HasMany(r => r.Menus) // One Restaurant has many Menus .WithOne(m => m.Restaurant) // One Menu belongs to one Restaurant .HasForeignKey(m => m.RestaurantId) // Foreign key property .OnDelete(DeleteBehavior.Cascade); // Optional: cascade delete if needed }
2. Update Your Controller Action to Work with EF Core
In EF 6, adding a child entity to a tracked parent's collection would automatically mark the child as Added. EF Core works similarly in most cases, but you need to ensure the child entity is properly associated with the parent, either via the foreign key or navigation property. Here's the revised action:
[HttpPost, Route("{id:int}")] public async Task<IActionResult> Post(int id, MenuViewModel menu) { if (!ModelState.IsValid) return BadRequest("Something is wrong with the model"); // No need to Include(r => r.Menus) unless you need to check existing menus var restaurant = await db.Restaurants.FirstOrDefaultAsync(r => r.Id == id); if (restaurant == null) return BadRequest("That restaurant does not exist"); // Map your ViewModel to the Menu entity (using AutoMapper or manual mapping) var menuModel = _mapper.Map<Menu>(menu); // Replace with your mapping logic // Associate the menu with the restaurant (pick one approach) // Approach 1: Set the foreign key directly menuModel.RestaurantId = id; // Approach 2: Add to the parent's collection (works if restaurant is tracked) // restaurant.Menus.Add(menuModel); // Explicitly add the menu to the DbSet (optional if using Approach 2, but safe for disconnected scenarios) db.Menus.Add(menuModel); await db.SaveChangesAsync(); return Ok(menuModel); }
3. Common Pitfalls to Avoid
- Don't set a non-default Id on the new Menu: If your
Menuuses an auto-incrementing primary key, make sure the mappedmenuModel.Idis0(the default forint). EF Core will treat any non-default Id as an existing entity and try to update it instead of inserting. - Avoid AsNoTracking() on the parent query: If you use
AsNoTracking()when fetching therestaurant, EF Core won't track changes to its collection, so addingmenuModeltorestaurant.Menuswon't trigger an insert. Use explicitdb.Menus.Add(menuModel)in that case. - Initialize the collection property: Always initialize
ICollection<Menu>inRestaurantto a newList<Menu>to avoid null reference exceptions when adding items.
Why This Works in EF Core vs EF 6
EF 6 had more implicit tracking logic for child entities added to tracked parent collections. EF Core is more explicit about entity state management, which makes it more predictable but requires you to be clear about how you associate and track entities. By setting the foreign key or adding the child to the parent's tracked collection, you're giving EF Core the context it needs to recognize the new entity and insert it correctly.
内容的提问来源于stack exchange,提问作者Rodolfo Gongora

