составной ключ как внешний ключ

Я использую Entity framework 4.1 в приложении MVC 3. У меня есть объект, в котором у меня есть первичный ключ, состоящий из двух столбцов (составной ключ). И это используется в другом объекте как внешний ключ. Как наладить отношения? В нормальных скнериях мы используем:

public class Category
{
    public string CategoryId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public string CategoryId { get; set; }

    public virtual Category Category { get; set; }
} 

но что, если у категории есть ключ с двумя столбцами?


person DotnetSparrow    schedule 25.03.2011    source источник


Ответы (2)


Вы можете использовать любой свободный API:

public class Category
{
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }

    public virtual Category Category { get; set; }
}

public class Context : DbContext
{
    public DbSet<Category> Categories { get; set; }
    public DbSet<Product> Products { get; set; }

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

        modelBuilder.Entity<Category>()
            .HasKey(c => new {c.CategoryId1, c.CategoryId2});

        modelBuilder.Entity<Product>()
            .HasRequired(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => new {p.CategoryId1, p.CategoryId2});

    }
}

Или аннотации данных:

public class Category
{
    [Key, Column(Order = 0)]
    public int CategoryId2 { get; set; }
    [Key, Column(Order = 1)]
    public int CategoryId3 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    [Key]
    public int ProductId { get; set; }
    public string Name { get; set; }
    [ForeignKey("Category"), Column(Order = 0)]
    public int CategoryId2 { get; set; }
    [ForeignKey("Category"), Column(Order = 1)]
    public int CategoryId3 { get; set; }

    public virtual Category Category { get; set; }
}
person Ladislav Mrnka    schedule 26.03.2011
comment
Нужно ли мне сохранять виртуальные свойства (общедоступную виртуальную категорию Category {get; set;}), а также аннотации данных? - person DotnetSparrow; 26.03.2011
comment
virtual по свойствам навигации необходимо для ленивой загрузки. virtual на скалярных свойствах помогает отслеживать изменения прикрепленных объектов. - person Ladislav Mrnka; 26.03.2011
comment
Что бы вы сделали, если бы имена столбцов в таблице внешнего ключа были другими, чем в родительской? Например, в продукте, как бы вы пометили атрибут ForeignKey, если бы имена столбцов выглядели так: PCategoryId2, PCategoryId3? - person ; 01.03.2012
comment
Что касается этой строки: .HasRequired(p => p.Category), но Product не имеет свойства Entity Catagory, но имеет два идентификатора, которые составляют составной ключ категории. Не могли бы вы объяснить, потому что я считаю, что он даже не скомпилируется ... Спасибо! - person gdoron is supporting Monica; 29.08.2012
comment
@gdoron: Product в моем ответе Category. - person Ladislav Mrnka; 29.08.2012
comment
@Ohhh извините, есть разрыв строки, поэтому я его не заметил ... Также как баннерная слепота :) Спасибо. - person gdoron is supporting Monica; 31.08.2012
comment
Привет, Ладислав Мрнка. В соответствии с имеющимися у вас навыками структуры сущностей, пожалуйста, посмотрите мой вопрос. Может быть, вы сможете, представьте мне решение. stackoverflow.com/q/23665133/1395101 Большое спасибо. - person Amin Ghaderi; 20.05.2014
comment
В вашем решении Data Annotation оба ключа происходят из одного и того же класса, но что, если составной PK ссылается на FK из разных таблиц? Например: в моем проекте составной PK CategoryTrans относится к PK Категории и ISO_Language PK. - person Luke; 21.05.2017
comment
@LadislavMrnka, что, если внешние ключи, на которые имеются ссылки, относятся к двум разным объектам? Я сейчас борюсь как раз с такой проблемой. - person Najeeb; 12.11.2017
comment
Можно ли преобразовать объект непримитивного значения, содержащий два поля? : public class MyId { public int CompanyId; public int UserId } - person zolty13; 29.07.2020

Я считаю, что самый простой способ - использовать аннотацию к данным в свойстве Navigation следующим образом: [ForeignKey("CategoryId1, CategoryId2")]

public class Category
{
    [Key, Column(Order = 0)]
    public int CategoryId1 { get; set; }
    [Key, Column(Order = 1)]
    public int CategoryId2 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    [Key]
    public int ProductId { get; set; }
    public string Name { get; set; }
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }

    [ForeignKey("CategoryId1, CategoryId2")]
    public virtual Category Category { get; set; }
}
person Christophe    schedule 26.08.2017
comment
Это отлично сработало. Я тоже предпочитаю использовать это в Navigation свойствах. Однако как я могу установить cascadeDelete: false только для этого свойства, а не для всего сайта? Спасибо - person RoLYroLLs; 22.03.2018
comment
В некоторых случаях внешний ключ также является частью составного ключа текущей таблицы. Так сработало. Другого пути (@Ladislov) не было. Я получил сообщение об ошибке: повторяющийся атрибут столбца - person D. Kermott; 21.06.2018
comment
RoLYroLLs: cascadeDelete устанавливается в файле миграции (после использования команды диспетчера пакетов add-migration). Пример: AddForeignKey (dbo.Product, GuidedActivityID, dbo.GuidedActivity, ID, cascadeDelete: false); - person Christophe; 19.08.2018