在EF中使用Composite主键作为外键

时间:2017-05-21 10:01:51

标签: c# sql asp.net-mvc

ASPNET MVC5网络应用程序。

模型

 public partial class Product
{
    public int? ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public bool IsDeleted { get; set; }
    public bool IsApproved { get; set; }
    public int CategoryID { get; set; }
    public virtual Category Category { get; set; }
    public virtual AspNetUsers User { get; set; }

我需要通过添加一个新的虚拟属性来修改此模型(从此开始工作)。

public virtual category_trans CategoryTrans { get; set; }

基于模型:

public class category_trans
{
    [Key]
    [Column(Order = 1)]
    public int category_id { get; set; }
    [Key]
    [Column(Order = 2)]
    public int language_id { get; set; }
    public string name { get; set; }
}

请注意,此表有一个复合PK。这是到目前为止在Product model类中成功实现的其他虚拟类别唯一明显的差异。

category_trans模型类在定义的许多查询中运行得很好但是,当我在产品模型中引入新的虚拟CategoryTrans时,甚至没有在任何查询中引用它,我得到以下错误:

  

System.Data.Entity.Core.EntityCommandExecutionException:执行命令定义时发生错误。有关详细信息,请参阅内部异常---> System.Data.SqlClient.SqlException:列名称无效' category_trans_category_id'。列名称无效' category_trans_language_id'。在System.Data.SqlClient.SqlCommand。<> c.b__174_0(任务1 result) in System.Threading.Tasks.ContinuationResultTaskFromResultTask 2.InnerInvoke()在System.Threading.Tasks.Task.Execute()

2 个答案:

答案 0 :(得分:0)

修改您的Product模型,如下所示:

     public partial class Product{

    public int? ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public bool IsDeleted { get; set; }
    public bool IsApproved { get; set; }
    [ForeignKey("CategoryTrans"), Column(Order = 1)]
    public int Cat_ID { get; set; }
    [ForeignKey("CategoryTrans"), Column(Order = 2)]
    public int Lang_ID { get; set; }
    public virtual Category Category { get; set; }
    public virtual AspNetUsers User { get; set; }

    public virtual category_trans CategoryTrans { get; set; }
   }

这将有效..

修改

下面给出的创建表的T_SQL脚本

CREATE TABLE [dbo].[Products] (
[Cat_ID]  INT            NOT NULL,
[Lang_ID]  INT            NOT NULL,
[ID]          INT            IDENTITY (1, 1) NOT NULL,
[Name]        NVARCHAR (MAX) NULL,
[Description] NVARCHAR (MAX) NULL,
[IsDeleted]   BIT            NOT NULL,
[IsApproved]  BIT            NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_dbo.Products_dbo.category_trans_Cat_ID_Lang_ID] FOREIGN KEY ([Cat_ID], [Lang_ID]) REFERENCES [dbo].[category_trans] ([category_id], [language_id]) ON DELETE CASCADE);




GO
CREATE NONCLUSTERED INDEX [IX_Cat_ID_Lang_ID]
ON [dbo].[Products]([Cat_ID] ASC, [Langu_ID] ASC);

答案 1 :(得分:0)

类别的翻译属于类别本身,而不属于产品。所以我建议将其添加到类别中。

模型类

public class Language
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public bool IsDeleted { get; set; }
    public bool IsApproved { get; set; }
    public int CategoryId { get; set; }
    public virtual Category Category { get; set; }
}

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<CategoryTrans> Translations { get; set; }
}

public class CategoryTrans
{
    public int CategoryId { get; set; }
    public virtual Category Category { get; set; }
    public int LanguageId { get; set; }
    public virtual Language Language { get; set; }
    public string Name { get; set; }
}

与FluentAPI的关系定义

public class Model1 : DbContext
{

    public Model1()
        : base( "name=Model1" )
    {
    }

    protected override void OnModelCreating( DbModelBuilder modelBuilder )
    {
        base.OnModelCreating( modelBuilder );

        var language = modelBuilder.Entity<Language>();
        language.HasKey( e => e.Id );
        language.Property( e => e.Name ).IsRequired().HasMaxLength( 100 );

        var product = modelBuilder.Entity<Product>();

        product.HasKey( e => e.Id );
        product.Property( e => e.Name ).IsRequired().HasMaxLength( 100 );
        product.Property( e => e.Description ).IsOptional().HasMaxLength( 1000 );
        product.Property( e => e.IsDeleted ).IsRequired();
        product.Property( e => e.IsApproved ).IsRequired();
        product.HasRequired( e => e.Category )
            .WithMany()
            .HasForeignKey( e => e.CategoryId )
            .WillCascadeOnDelete( false );

        var category = modelBuilder.Entity<Category>();

        category.HasKey( e => e.Id );
        category.Property( e => e.Name ).IsRequired().HasMaxLength( 100 );
        category.HasMany( e => e.Translations )
            .WithRequired( e => e.Category )
            .HasForeignKey( e => e.CategoryId )
            .WillCascadeOnDelete( true );

        var categoryTrans = modelBuilder.Entity<CategoryTrans>();
        // composite key
        categoryTrans.HasKey( e => new { e.CategoryId, e.LanguageId } );
        categoryTrans.HasRequired( e => e.Language )
            .WithMany()
            .HasForeignKey( e => e.LanguageId )
            .WillCascadeOnDelete( false );

    }
}

由此产生的迁移

public partial class InitialCreate : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.Languages",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false, maxLength: 100),
                })
            .PrimaryKey(t => t.Id);

        CreateTable(
            "dbo.Products",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false, maxLength: 100),
                    Description = c.String(maxLength: 1000),
                    IsDeleted = c.Boolean(nullable: false),
                    IsApproved = c.Boolean(nullable: false),
                    CategoryId = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("dbo.Categories", t => t.CategoryId)
            .Index(t => t.CategoryId);

        CreateTable(
            "dbo.Categories",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false, maxLength: 100),
                })
            .PrimaryKey(t => t.Id);

        CreateTable(
            "dbo.CategoryTrans",
            c => new
                {
                    CategoryId = c.Int(nullable: false),
                    LanguageId = c.Int(nullable: false),
                    Name = c.String(),
                })
            .PrimaryKey(t => new { t.CategoryId, t.LanguageId })
            .ForeignKey("dbo.Languages", t => t.LanguageId)
            .ForeignKey("dbo.Categories", t => t.CategoryId, cascadeDelete: true)
            .Index(t => t.CategoryId)
            .Index(t => t.LanguageId);

    }

    public override void Down()
    {
        DropForeignKey("dbo.Products", "CategoryId", "dbo.Categories");
        DropForeignKey("dbo.CategoryTrans", "CategoryId", "dbo.Categories");
        DropForeignKey("dbo.CategoryTrans", "LanguageId", "dbo.Languages");
        DropIndex("dbo.CategoryTrans", new[] { "LanguageId" });
        DropIndex("dbo.CategoryTrans", new[] { "CategoryId" });
        DropIndex("dbo.Products", new[] { "CategoryId" });
        DropTable("dbo.CategoryTrans");
        DropTable("dbo.Categories");
        DropTable("dbo.Products");
        DropTable("dbo.Languages");
    }
}