0

Я пишу приложение ASP.NET MVC 5 с Entity Framework 6 Code First, и у меня проблема с внешними ключами. Чтобы проиллюстрировать проблему, я показываю две таблицы: «Страна и валюта».Проблемы с использованием атрибутов внешнего ключа Entity Framework

Модель Страна представляет собой таблицу со странами и их атрибуты:

[Table("dbo.Country")] 
public class Country { 
    [Key] 
    [StringLength(2)] 
    [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))] 
    public string iso { get; set; } 

    [Required] 
    [StringLength(80)] 
    [Display(Name = "L_Country", ResourceType = typeof(ResxGlobal))] 
    public string name { get; set; } 

    [Required] 
    [StringLength(80)] 
    public string nicename { get; set; } 

    [StringLength(3)] 
    [Display(Name = "L_Country_ISO3", ResourceType = typeof(ResxGlobal))] 
    public string iso3 { get; set; } 

    public short? numcode { get; set; } 

    [Display(Name = "L_DialCode", ResourceType = typeof(ResxGlobal))] 
    public int phonecode { get; set; } 
} 

И у меня есть отдельная таблица с именем Валюта, которая перечисляет валюту страны атрибуты с его PK быть две буквы ISO код страны, который в то же время FK к столу Страна:

[Table("dbo.Currency")] 
public class Currency { 
    [Key] 
    [StringLength(2)] 
    [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))] 
    public string CountryCode { get; set; } 

    [Required, StringLength(100)] 
    [Display(Name="Currency name")] 
    public string CurrencyName { get; set; } 

    [Required, StringLength(3)] 
    [Display(Name="Currency code")] 
    public string CurrencyCode { get; set; } 

    [StringLength(5)] 
    public string Symbol { get; set; } 

    //[ForeignKey("CountryCode")] 
    public virtual Country Country {get; set; } 
} 

до сих пор так хорошо, так что теперь посмотрите на 2-й модели (валюты). ЕСЛИ я включаю атрибут ForeignKey, указывающий, что CountryCode PK также является FK, который будет использоваться страной свойств навигации и будет выполнять миграцию, тогда диаграмма SQL Server показывает взаимосвязь между страной и валютой с терминатором PK (их небольшой ключ) при оба конца отношения, тогда как обычно значок маленького ключа отображается на таблице PK и значок маленькой бесконечности, показанный на конце FK отношения. Это я нашел странным.

Я вернул миграцию, удалил (закомментировал) атрибут внешнего ключа и снова обновил базу данных. На этот раз отношения показали, как я ожидал, что PK с ключевым и другим концом с иконкой бесконечности. Однако в таблице показан дополнительный столбец, который я не указал в моей модели. Я вручную отредактировал миграцию, чтобы опустить дополнительный столбец с именем «Country_iso», который не был указан в моей модели.

Удалив (из кода миграции) нежелательный столбец Country_iso перед обновлением базы данных, я обнаружил, что при попытке использовать контекст базы данных для извлечения валюты я получаю ошибку «Недопустимое имя столбца Country_iso». Где на земле он настаивает на получении колонки, которой у меня нет? Я не вижу ссылки на это где-нибудь, он скрыт где-то в каком-то файле метаданных?

Итак, что было бы правильным или правильным способом иметь отношения FK (от одного до одного и от одного до многих) в модели?

ответ

1

Несколько недель назад я нашел противоречивую информацию об использовании атрибута ForeignKey, поэтому я придерживался той, которая казалась наиболее логичной.

Однако, я сделал еще один эксперимент, а затем переехал ForeignKey атрибут в COUNTRYCODE собственности валюты таблицы/модели следующим образом:

[Key] 
[ForeignKey("Country")] 
[StringLength(2)] 
[Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))] 
public string CountryCode { get; set; } 
     : 
public virtual Country Country {get; set; } 

так что теперь на поле PK (т.е. также является FK) и указывает на соответствующее навигационное свойство (Страна).

В результате миграционный «скрипт» не вводит какой-либо фантомный столбец (прежний столбец Country_iso), и поиск информации также не вызывает ошибок.

0

Включение ограничения внешнего ключа в ваш первичный ключ не кажется хорошей идеей.

Рассмотрим ситуацию, когда вы удаляете Country запись, база данных не может обеспечить ограничение FK в валюты таблицы, потому что она не может NULL в поле FK (Это также первичный ключ). Ваш Валюта запись будет продолжать ссылаться на то, что не существует.

Если вы не предоставили внешний ключ, и нельзя было решить проблему с именем ваших свойств, EF сгенерировал для вас столбец Country_iso.

У вас есть свойство CurrencyCode в вашей таблице валют, которую вы можете использовать в качестве первичного ключа.

[Table("dbo.Currency")] 
public class Currency { 

    [StringLength(2)] 
    [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))] 
    public string CountryCode { get; set; } 

    [Required, StringLength(100)] 
    [Display(Name="Currency name")] 
    public string CurrencyName { get; set; } 

    [Key] 
    [Required, StringLength(3)] 
    [Display(Name="Currency code")] 
    public string CurrencyCode { get; set; } 

    [StringLength(5)] 
    public string Symbol { get; set; } 

    [ForeignKey("CountryCode")] 
    public virtual Country Country {get; set; } 
} 

Это будет работать для каждой страны, имеющей только одну валюту и наоборот (1: 1). Если вы хотите поддерживать несколько стран с одинаковой валютой (M: 1), вы хотите, чтобы каждая страна ссылалась на запись в валюте.

[Table("dbo.Country")] 
public class Country{ 
    [Key] 
    [StringLength(2)] 
    [Display(Name = "L_Country_ISO2", ResourceType = typeof(ResxGlobal))] 
    public string iso { get; set; } 

    [Required] 
    [StringLength(80)] 
    [Display(Name = "L_Country", ResourceType = typeof(ResxGlobal))] 
    public string name { get; set; } 

    [Required] 
    [StringLength(80)] 
    public string nicename { get; set; } 

    [StringLength(3)] 
    [Display(Name = "L_Country_ISO3", ResourceType = typeof(ResxGlobal))] 
    public string iso3 { get; set; } 

    public short? numcode { get; set; } 

    [Display(Name = "L_DialCode", ResourceType = typeof(ResxGlobal))] 
    public int phonecode { get; set; } 

    public string CurrencyCode { get; set; } 

    [ForeignKey("CurrencyCode")] 
    public virtual Currency Currency { get; set; } 
} 

[Table("dbo.Currency")] 
public class Currency{ 
    [Key] 
    [Required, StringLength(3)] 
    [Display(Name = "Currency code")] 
    public string CurrencyCode { get; set; } 

    [Required, StringLength(100)] 
    [Display(Name = "Currency name")] 
    public string CurrencyName { get; set; } 

    [StringLength(5)] 
    public string Symbol { get; set; } 

    public virtual ICollection<Country> Countries { get; set; } 
} 
Смежные вопросы