繁体   English   中英

通过具有多对多关系的实体框架代码优先方法为 SQL Server 播种

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

我首先将 EF6 代码与 ASP.NET Web API 一起使用。

假设有两个模型类

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

数据库上下文

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

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

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 不知道家具。 或者我应该以另一种方式定义模型?

谢谢你。


编辑:我检查了 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)
);

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

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

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

我仍然认为我可以在没有异常的情况下运行您的代码(并且没有得到我后来注意到的第二个“木”关联),这很奇怪。 我想知道这是否是 .Net 4.5.2 问题。

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

无论如何,回到您的问题,要显示“木头”两次,请将您的 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();

但是这样做,它会显示重复的名称“wood”,在您的 MAterial 表中具有不同的 Id

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM