1

I just added an EF entity called PurchaseOrderProduct, which creates a many-to-many link between my PurchaseOrders and Products.

[PrimaryKey(nameof(PurchaseOrderId), nameof(ProductId))]
public class PurchaseOrderProduct
{
    public int PurchaseOrderId { get; set; }
    public PurchaseOrder PurchaseOrder { get; set; }

    public int ProductId { get; set; }
    public Product Product { get; set; }
}

However, I don't quite understand the generated migration script. I want the primary key to be a compound key based on the PurchaseOrderId and ProductId columns. But why does it also create an additional IX_PurchaseOrderProducts_ProductId index on my ProductId column?

/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateTable(
        name: "PurchaseOrderProducts",
        columns: table => new
        {
            PurchaseOrderId = table.Column<int>(type: "int", nullable: false),
            ProductId = table.Column<int>(type: "int", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_PurchaseOrderProducts", x => new { x.PurchaseOrderId, x.ProductId });
            table.ForeignKey(
                name: "FK_PurchaseOrderProducts_Products_ProductId",
                column: x => x.ProductId,
                principalTable: "Products",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            table.ForeignKey(
                name: "FK_PurchaseOrderProducts_PurchaseOrders_PurchaseOrderId",
                column: x => x.PurchaseOrderId,
                principalTable: "PurchaseOrders",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

    migrationBuilder.CreateIndex(
        name: "IX_PurchaseOrderProducts_ProductId",
        table: "PurchaseOrderProducts",
        column: "ProductId");
}
0

1 Answer 1

3

It seems to be a consistent pattern. I can reproduce it in another many-to-many relationship, also without an explicit junction entity (like PurchaseOrderProduct). It's also shown (but not explained) in EF's own many-to-many example.

An educated guess as to why EF does it is that the composite primary key on PurchaseOrderId (as the first field) and ProductId can't be used effectively for index seeks on ProductId.

Hence a separate index on ProductId, which does allow effective query plans involving searches on ProductId only.

Sign up to request clarification or add additional context in comments.

4 Comments

Yes, that's what I was thinking the most likely reason also. The compound index can be used for PurchaseOrderId. It was just a bit unexpected.
Probably Relationships - Conventions - Indexes section is a bit better reference, especially the beginning: "By convention, EF creates a database index for the property or properties of a foreign key." and the end Note: "EF does not create indexes for properties that are already covered by an existing index or primary key constraint."
@Ivan Yeah, I thought of that, but at the end they say "For composite foreign keys, an index is created covering all the foreign key columns." where they only show a composite index. I'd love to find an explicit mention of this additional single-column index.
Agreed hundred percent! I also wish they restructure the documentation in way that all such things are explained in a single place, rather than jumping here and there, Notes etc.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.