按实体框架播种SQL Server以代码优先的方式实现多对多关系

时间:2015-06-26 18:33:19

标签: c# asp.net sql-server entity-framework ef-code-first

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

假设有两个模型类

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; }
}

在初始化类中,

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();
}

我遇到了运行时错误抱怨“无法从固定大小的数组中删除某个项目”。很明显,该程序试图在将木材添加到coffeeTable之前从椅子上移除木材。所以我将初始化更改为使用Lists,如

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

在那之后,我可以清楚地看到木材确实是从家具的RawMaterials中移除的。

我也尝试通过

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

结果仍然相同。

所以我的问题是:我如何添加测试数据,以便木材和咖啡桌上都有木材?我知道这通常不是多少对多关系的定义,因为RawMaterial不知道家具。或者我应该用另一种方式定义模型?

谢谢。

编辑: 我检查SQL Server对象资源管理器中的数据库表,而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)
);

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

2 个答案:

答案 0 :(得分:1)

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

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"));
}

这会创建一个连接两个实体的联结表FurnitureRawMaterial

我仍然觉得奇怪的是,我可以在没有得到异常的情况下运行你的代码(并且没有得到第二个&#34; wood&#34;我后来注意到的关联)。我想知道它是否是.Net 4.5.2问题。

答案 1 :(得分:0)

根据您的描述,Furniture和RawMaterial之间的关系看起来像很多对很多而不是一对多。一个家具可以有许多原材料,同时原材料可以属于多个家具。

无论如何,回到你的问题,展示&#39; wood&#39;两次,将Seed方法更改为:

        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();

不管这样做,它会显示重复的名称&#34; wood&#34;,在您的MAterial表中显示不同的ID