通过具有多对多关系的 Entity Framework 代码优先方法为 SQL 服务器播种

Seeding SQL Server by Entity Framework code-first approach with many-to-many relationship

我首先在 ASP.NET Web API 中使用 EF6 代码。

假设有两个模型classes

public class RawMaterial {
    public int ID { get; set; }
    public string Name { get; set; }
}
public class Furniture {
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<RawMaterial> RawMaterials { get; set; }
}

DbContext

public class FurnitureContext : DbContext {
    public DbSet<RawMaterial> RawMaterials { get; set; }
    public DbSet<Furniture> Furnitures { get; set; }
}

并且在初始化器中 class,

protected override void Seed (FurnitureContext context) {
    var glass = new RawMaterial { Name = "glass" };
    var wood = new RawMaterial { Name = "wood" };
    var paint = new RawMaterial { Name = "paint" };
    context.RawMaterials.AddRange(new RawMaterial[] { glass, wood, paint });

    var chair = new Furniture {
        Name = "chair",
        RawMaterials = new RawMaterial[] { wood, paint }
    };
    var coffeeTable = new Furniture {
        Name = "coffee table",
        RawMaterials = new RawMaterial[] { wood, glass }
    };
    context.Furnitures.AddRange(new Furnitures[] { chair, coffeeTable });

    context.SaveChanges();
}

我遇到 运行 时间错误,抱怨 "an item cannot be removed from fixed size array"。很明显,该程序试图在将木头添加到咖啡桌之前从椅子上取下木头。所以我更改了初始化以使用列表,如

var chair = new Furniture {
    Name = "chair",
    RawMaterials = new List<RawMaterial> { wood, paint }
};

在那之后,我可以清楚地看到其中一件家具的原材料确实被去除了木材。

我还尝试通过

从上下文中选择木材
var chair = new Furniture {
    Name = "chair",
    RawMaterials = new RawMaterial[] {
        context.RawMaterials.Where(r => r.Name == wood.Name).FirstOrDefault()
    }
};

结果还是一样

所以我的问题是:如何添加测试数据,使椅子和咖啡桌中都存在木头?我知道这通常不是定义的多对多关系,因为 RawMaterial 不知道 Furniture。还是我应该以其他方式定义模型?

谢谢。


编辑: 我检查了 SQL Server Object Explorer 中的数据库表,RawMaterial 的 SQL 是

CREATE TABLE [dbo].[RawMaterials] (
    [ID]           INT            IDENTITY (1, 1) NOT NULL,
    [Name]         NVARCHAR (MAX) NULL,
    [Furniture_ID] INT            NULL,
    CONSTRAINT [PK_dbo.RawMaterials] PRIMARY KEY CLUSTERED ([ID] ASC),
    CONSTRAINT [FK_dbo.RawMaterials_dbo.Furnitures_Furniture_ID] FOREIGN KEY ([Furniture_ID]) REFERENCES [dbo].[Furnitures] ([ID])
);


GO
CREATE NONCLUSTERED INDEX [IX_Furniture_ID]
    ON [dbo].[RawMaterials]([Furniture_ID] ASC);

而家具的 SQL 是

CREATE TABLE [dbo].[Furnitures] (
    [ID]   INT            IDENTITY (1, 1) NOT NULL,
    [Name] NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_dbo.Furnitures] PRIMARY KEY CLUSTERED ([ID] ASC)
);

所以基本上 entity framework 没有按照我需要的方式创建数据库。这就是为什么我不能在椅子和咖啡桌上都添加木材。我应该如何修改实体模型?

根据您的描述,Furniture 和 RawMaterial 之间的关系在我看来是多对多而不是一对多。一个家具可以有多个rawmaterial,同时一个rawmaterial可以属于多个家具

无论如何,回到你的问题,要显示 'wood' 两次,将你的种子方法更改为:

        var chair = new Furniture
        {
            Name = "chair",
            RawMaterials = new List<RawMaterial>
            {
                new RawMaterial {
                    Name = "paint",
                    FurnitureId = 1
                },
                new RawMaterial {
                    Name = "wood",
                    FurnitureId = 1
                },
            }
        };
        var coffeeTable = new Furniture
        {

            Name = "coffee table",
            RawMaterials = new List<RawMaterial>
            {
                new RawMaterial {
                    Name = "glass",
                    FurnitureId = 2
                },
                new RawMaterial {
                    Name = "wood",
                    FurnitureId = 2
                },
            }
        };
        context.Furnitures.AddRange(new Furniture[] { chair, coffeeTable });
        context.SaveChanges();

但是这样做,它会显示重复的名称 "wood",在您的 MAterial table

中具有不同的 ID

我太专注于您报告的错误,以至于忘记查看映射。但是一旦我这样做了,它突然显得很简单。正如您正确指出的那样,这是一个多对多的关联,但它没有像一个那样映射。这是您正确映射它的方式:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<Furniture>()
                .HasMany(f => f.RawMaterials)
                .WithMany()
                .Map(m => m.MapLeftKey("FurnitureId")
                           .MapRightKey("RawMaterialId")
                           .ToTable("FurnitureRawMaterial"));
}

这将创建一个连接两个实体的连接点 table FurnitureRawMaterial

我仍然认为我可以 运行 你的代码而没有得到异常(并且没有得到我后来注意到的第二个 "wood" 关联)是很奇怪的。我想知道这是否是 .Net 4.5.2 问题。