5

Мне кажется, как атрибут ForeignKey не работает для меня, но я предполагаю, что я использую это неправильно;)Entity Framework Code First иностранных Ключевой вопрос

Это проще объяснить с кодом:

public class BaseCard 
{ 
    public int Id {get ; set; } 
    public int BaseCardId { get; set; } 

    public List<Skill> Skills { get; set; } 
} 

public class Skill 
{ 
    public int Id { get; set; } 

    public int BaseCardId { get; set; } 
    [ForeignKey("BaseCardId")] 
    public BaseCard BaseCard { get; set; } 
} 

Когда я пытаюсь заполнить эти объекты с помощью метода семян, я получаю эту ошибку:

INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Skills_dbo.BaseCards_BaseCardId". The conflict occurred in database "Database", table "dbo.BaseCards", column 'Id'.

Мне кажется, как и ForeignKey в Skill попыток, чтобы указать на IdBaseCards колонка вместо BaseCardId колонок, и я не могу понять, почему ..

Если я пытаюсь удалить «нормальное» Id свойства BaseCard и установите BaseCardId в качестве PK (с атрибутом [Key]), я получаю следующее сообщение об ошибке:

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

кто-нибудь знает, как я могу получить этот код, чтобы работать так, свойство BaseCardId из Skill класса будет указывать на BaseCardId свойство BaseCard, вместо видимо, Id недвижимость?

Заранее благодарен!

+0

Почему у вас есть поля Id и BaseCardId в вашем объекте BaseCard? – Sefa

+0

@vgSefa Я установил BaseCardId на определенное число. И Id, как я думал, автоматически генерировался. Но это должен быть конкретный номер. Можно ли установить Id на определенный номер? – Rob

+0

@vgSefa Я попытался установить Id вместо BaseCardId на нужный мне номер, к сожалению, это не работает. – Rob

ответ

7

Возможно установить самостоятельно идентификатор BaseCard, но сначала вы должны использовать Fluent Api, чтобы указать это и отношения «один ко многим». Кроме того, вам не нужно указывать атрибуты в ваших классах моделей. Ваша модель классы будут выглядеть так:

public class BaseCard 
{ 
    public int Id {get ; set; } 

    public virtual ICollection<Skill> Skills { get; set; } 
} 

public class Skill 
{ 
    public int Id { get; set; } 

    public int BaseCardId { get; set; } 
    public virtual BaseCard BaseCard { get; set; } 
} 

Как вы можете видеть, я изменить навигационные свойства, как virtual.If вы определяете свойство навигации в качестве виртуального EF будет во время выполнения создать новый класс (динамический прокси), полученные из ваш класс BaseCard и используйте его вместо этого (то же самое происходит со Skill). Этот новый динамически созданный класс содержит логику для загрузки свойства навигации при первом доступе. Эта функция называется ленивой загрузкой. Она позволяет Entity Framework избежать загрузки всего дерева зависимых объектов, которые не нужны из базы данных. Вы можете найти более подробную информацию об этой теме в этих сообщениях: Why Navigation Properties are virtual by default in EF и Entity Framework 4.1 Virtual Properties.

Другое изменение, которое я использую в вашей модели, это использование ICollection <> введите вместо этого список <> в свойстве Skills. Как вы можете видеть в этом post, интерфейс является хорошей практикой в ​​этом случае. Он позволяет вам позже указать требуемую реализацию (может быть список <>).

Теперь, возвращаясь к вашей проблеме, в вашем классе Context вам необходимо переопределить метод OnModelCreating, чтобы указать отношение и столбец no autogenerate для Id в таблице BaseCards.

public class YourContext : DbContext 
{ 
    public DbSet<BaseCard> BaseCards { get; set; } 

    public DbSet<Skill> Skill { get; set; } 

    //... 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     // Configure the primary key for BaseCard 
     modelBuilder.Entity<BaseCard>().HasKey(t => t.Id); 
     //specify no autogenerate the Id Column 
     modelBuilder.Entity<BaseCard>().Property(b => b.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); 

     //one-to-many relationship 
     modelBuilder.Entity<Skill>().HasRequired(c => c.BaseCard) 
       .WithMany(s => s.Skills) 
       .HasForeignKey(c => c.BaseCardId); 
    } 
} 

С этой конфигурацией вы должны всегда устанавливать идентификатор объектов BaseCard с другим значением.

Если вы предпочитаете использовать аннотации данных можно указать тот же на этом пути:

public class BaseCard 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] 
    public int Id { get; set; } 
    public virtual ICollection<Skill> Skills { get; set; } 
} 

public class Skill 
{ 
    [Key] 
    public int Id { get; set; } 

    public int BaseCardId { get; set; } 

    [ForeignKey("BaseCardId")] 
    public virtual BaseCard BaseCard { get; set; } 
} 

Моя рекомендация заключается в использовании Fluent API, это более гибкий, и вы не должны трогать классы моделей. Вы можете увидеть некоторые полезные объяснения в этом post

+0

Спасибо за подробное объяснение и дайте мне более 1 вариант. Я собираюсь сделать еще несколько исследований и посмотреть, какой из них я буду использовать. Еще раз спасибо. – Rob

+0

octavioccl, Когда я использую Fluent API, я получаю сообщение об ошибке при вызове update-database -verbose. Это вызывает метод Seed из миграции. Здесь я пытаюсь заполнить базу данных, но при сохранении BaseCard в контексте я получаю следующую ошибку: Не могу вставить явное значение для столбца идентификатора в таблице «BaseCards», когда IDENTITY_INSERT установлен в OFF. Это связано с тем, что метод OnModelCreating не вызывается при переходе на БД? Как еще вы предлагаете мне заполнить базу данных? – Rob

+0

Вы удалили старую базу данных и создали новую ?. Я думаю, это связано с тем, что у вас есть старая база данных с столбцом Id BaseCards как Identity. – octavioccl

Смежные вопросы