DEV Community

mohamed Tayel
mohamed Tayel

Posted on

EFCore Tutorial P3:One-to-One, One-to-Many, and Many-to-Many

In this article, we’ll explore how to configure relationships between entities in Entity Framework Core (EF Core). We will use the entities Product, Category, Inventory, and Supplier. We will guide you through configuring:

  1. One-to-One relationships
  2. One-to-Many relationships
  3. Many-to-Many relationships with a custom join table

We’ll also apply the relationships using Attributes and Fluent API, and we’ll go step-by-step to ensure the configuration is clear.


Step-by-Step: Configuring Relationships

Step 1: Open the ProductData Project

  • Open Visual Studio.
  • Navigate to the Solution Explorer and locate the ProductData project, where we will configure the relationships.

Step 2: Add the Supplier Class to the ProductDomain Project

  • In ProductDomain, right-click and add a new class called Supplier.
  • Define the class as follows:
public class Supplier
{
    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<ProductSupplier> ProductSuppliers { get; set; } // Many-to-Many relationship with Products
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Modify the Product Class

  • Open the Product class in the ProductDomain project.
  • Modify it to include the ProductSuppliers collection for the Many-to-Many relationship:
public class Product
{
    public int Id { get; set; }

    [Required]
    [MaxLength(100)]
    public string Name { get; set; }

    [Range(0, 10000)]
    public decimal Price { get; set; }

    public int CategoryId { get; set; }

    [ForeignKey("CategoryId")]
    public Category Category { get; set; }

    public Inventory Inventory { get; set; } // One-to-One relationship with Inventory

    public ICollection<ProductSupplier> ProductSuppliers { get; set; } // Many-to-Many relationship with Suppliers
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Modify the Inventory and Category Classes

  • In the ProductDomain project, ensure the Inventory and Category classes are correctly set up for their relationships.

Category Class:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Product> Products { get; set; } // One-to-Many relationship with Products
}
Enter fullscreen mode Exit fullscreen mode

Inventory Class:

public class Inventory
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public Product Product { get; set; } // One-to-One relationship with Product
    public int Quantity { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Add the ProductSupplier Class

  • In the ProductDomain project, create a new class called ProductSupplier to act as the join table for the Many-to-Many relationship between Product and Supplier:
public class ProductSupplier
{
    public int Id { get; set; } // Primary key for the join table
    public int ProductId { get; set; }
    public Product Product { get; set; }
    public int SupplierId { get; set; }
    public Supplier Supplier { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Update the AppDbContext Class in ProductData Project

  • Now, open the ProductData project and modify the AppDbContext class to configure these relationships using Fluent API:
using Microsoft.EntityFrameworkCore;
using ProductDomain;

namespace ProductData
{
    public class AppDbContext : DbContext
    {
        public DbSet<Product> Products { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Inventory> Inventories { get; set; }
        public DbSet<Supplier> Suppliers { get; set; }
        public DbSet<ProductSupplier> ProductSuppliers { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"your-connection string ");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // One-to-One relationship between Product and Inventory
            modelBuilder.Entity<Product>()
                .HasOne(p => p.Inventory)
                .WithOne(i => i.Product)
                .HasForeignKey<Inventory>(i => i.ProductId);

            // One-to-Many relationship between Category and Products
            modelBuilder.Entity<Category>(entity =>
            {
                entity.HasKey(c => c.Id); // Set primary key
                entity.Property(c => c.Name)
                      .IsRequired()
                      .HasMaxLength(50); // Limit the Name length to 50 characters

                entity.HasMany(c => c.Products)
                      .WithOne(p => p.Category)
                      .HasForeignKey(p => p.CategoryId);
            });

                   // Many-to-Many relationship between Products and Suppliers with custom join table
       modelBuilder.Entity<ProductSupplier>()
.HasKey(ps => ps.Id); // Define primary key for ProductSupplier

       modelBuilder.Entity<ProductSupplier>()
           .HasOne(ps => ps.Product) // Configure the relationship to Product
           .WithMany(p => p.ProductSuppliers)
           .HasForeignKey(ps => ps.ProductId);

       modelBuilder.Entity<ProductSupplier>()
           .HasOne(ps => ps.Supplier) // Configure the relationship to Supplier
           .WithMany(s => s.ProductSuppliers)
           .HasForeignKey(ps => ps.SupplierId);

       modelBuilder.Entity<ProductSupplier>()
           .ToTable("ProductSuppliers"); // Define the table name
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Apply the Migrations

  • Once all relationships are configured, open the Package Manager Console in Visual Studio:
    1. Go to Tools > NuGet Package Manager > Package Manager Console.
    2. Ensure the Default Project is set to ProductData.
    3. Run the following commands to create and apply the migration:
Add-Migration ConfigureRelationships
Update-Database
Enter fullscreen mode Exit fullscreen mode

This will create the necessary database tables and relationships for Product, Category, Inventory, Supplier, and the ProductSuppliers join table.


Explanation of Relationships

One-to-One Relationship (Product and Inventory):

A product can have exactly one inventory entry, and an inventory entry is linked to exactly one product. We configure this using the HasOne and WithOne Fluent API methods.

One-to-Many Relationship (Category and Products):

Each category can contain multiple products, while each product belongs to only one category. This is achieved using HasMany and WithOne methods.

Many-to-Many Relationship (Product and Supplier):

A product can have multiple suppliers, and a supplier can supply multiple products. The join table ProductSuppliers is explicitly defined with Id, ProductId, and SupplierId.


Conclusion

In this article, we walked through configuring One-to-One, One-to-Many, and Many-to-Many relationships in EF Core using our entities Product, Category, Inventory, and Supplier. We provided a step-by-step guide on how to modify the ProductData and ProductDomain projects, apply migrations, and update the database schema to reflect the relationships.
Source code EFCoreDemo

Top comments (0)