简体   繁体   中英

Use Composite Primary key as Foreign key in EF

ASPNET MVC5 web application.

Model

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

I need to modify this model (working since ever) by including a new virtual property.

public virtual category_trans CategoryTrans { get; set; }

based on model:

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

Please note this table has a composite PK. This is the only apparent difference with the other virtual categories successfully implemented in Product model class up to now.

category_trans model class works just fine in many queries as defined but, as I introduce the new virtual CategoryTrans in Product model, without even referencing it in any query , i get following error:

System.Data.Entity.Core.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Invalid column name 'category_trans_category_id'. Invalid column name 'category_trans_language_id'. in System.Data.SqlClient.SqlCommand.<>c.b__174_0(Task 1 result) in System.Threading.Tasks.ContinuationResultTaskFromResultTask 2.InnerInvoke() in System.Threading.Tasks.Task.Execute()

Modify your Product Model as follows :

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

This will work..

EDIT

T_SQL script of the created table given below

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

The translation for the categories belong to the category itself and not to the product. So I recommend to add that to the category.

Model classes

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

Definition of the relation with 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 );

    }
}

And the resulting migration

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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