EF Code First Дополнительная навигация - PullRequest
2 голосов
/ 13 декабря 2011

У меня есть следующие объекты POCO:

public class Chat
{
    public int ChatID { get; set; }
    public virtual Audio ChatAudio { get; set; }
    public virtual Video ChatVideo { get; set; }
}

public class Audio
{
    public int AudioID { get; set; }
    public int ChatID { get; set; }
    public Chat Chat { get; set; }
    public DateTime Recorded { get; set; }
}

public class Video
{
    public int VideoID { get; set; }
    public int ChatID { get; set; }
    public Chat Chat { get; set; }
    public DateTime Recorded { get; set; }
}

Администратор баз данных имеет следующие настройки таблиц

-Chat-
ChatID

-Audio-
AudioID
ChatID <-- FK, Unique Index
Recorded

-Video-
VideoID
ChatID <-- FK, Unique Index
Recorded

Так что это отношение 1 к факультативному.Он не хочет создавать пустые столбцы для аудио или видео на столе чата.Как я могу заставить это работать в EF Code First?Единственный способ заставить это работать - создать коллекции навигационных свойств и определить HasMany ().Я не хочу этого.Я хочу иметь возможность:

if(Chat.Audio == null || Chat.Video==null)
{
// Do stuff
}

Я ценю любые ваши предложения.

Ответы [ 4 ]

2 голосов
/ 13 декабря 2011

Нет, вы не можете сопоставить эту модель со своей схемой базы данных с Entity Framework (независимо от того, какая версия до текущей версии 4.2).

Основная причина в том, что EF не поддерживает ограничения уникального ключа, что означает: сопоставление ссылочного свойства навигации со столбцом в базе данных, который имеет ограничение уникального ключа. EF всегда будет считать такой столбец FK в базе данных не уникальным и не увидит ограничение уникального ключа. Таким образом, концы двух ассоциаций в Chat должны быть коллекциями. Или вы не могли бы выставить их в своей модели, но внутренне EF будет рассматривать их всегда как «множество» концов.

Единственное истинное отношение один к одному, которое поддерживает EF, - это общие отношения первичного ключа. Это означает, что у вас нет столбца ChatId в таблице Audio и Video, но вместо этого столбцы первичного ключа AudioId и VideoId являются столбцами внешнего ключа в таблице Chat в то же время время. Очевидно, что столбцы первичного ключа не могут быть всеми автоматически сгенерированными идентификаторами в базе данных, чтобы сделать возможным такое сопоставление.

Отличное описание как картографических стратегий «один к одному», так и сравнение их преимуществ и ограничений можно найти здесь:

0 голосов
/ 13 декабря 2011

Другой вариант, поскольку видео и аудио «принадлежат» чату, заключается в использовании сложной структуры.Не давайте видео и аудио свои собственные идентификаторы, они будут сопоставлены с таблицей чата.Я знаю, что некоторые DBA ненавидят эту идею, потому что это необязательные данные.Но технически это ничем не отличается от непосредственного сохранения адреса в таблице клиентов.Все зависит от того, насколько нормализуется ваша база данных.

0 голосов
/ 13 декабря 2011

Позвольте мне убедиться, что я правильно понимаю:

В чате есть ноль или одно аудио, и ноль или одно видео.

Аудио, если оно существует, принадлежит ровно 1 чату. Видео, если оно существует, принадлежит ровно 1 чату.

Это правильно? Если да, будет ли ваш администратор БД возражать против такой схемы?

-Chat-
ChatID

-Audio-
ChatID <-- FK, Primary Key
Recorded

-Video-
ChatID <-- FK, Primary Key
Recorded

Если это так, я думаю, вы можете сделать это:

public class Chat
{
    public int ChatID { get; set; }
    public virtual Audio ChatAudio { get; set; }
    public virtual Video ChatVideo { get; set; }
}

public class Audio
{
    public int ChatID { get; set; }
    public virtual Chat Chat { get; set; }
    public DateTime Recorded { get; set; }
}

public class Video
{
    public int ChatID { get; set; }
    public virtual Chat Chat { get; set; }
    public DateTime Recorded { get; set; }
}

...

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Audio>().HasKey(a => a.ChatID);
    modelBuilder.Entity<Video>().HasKey(a => a.ChatID);
    var chat = modelBuilder.Entity<Chat>();
    chat
        .HasOptional(c => c.ChatAudio)
        .WithRequired(a => a.Chat);

    chat
        .HasOptional(c => c.ChatVideo)
        .WithRequired(a => a.Chat);
    ...
 }
0 голосов
/ 13 декабря 2011

Вы можете «обмануть» и настроить отношение «один ко многим», применяя один к одному в объектной модели (также в инициализаторе datacontext вы можете вручную добавить уникальное ограничение в базу данных).

Это будет выглядеть примерно так

public class Chat 
{ 
    public int ChatID { get; set; } 
    protected virtual ICollection<Audio> HiddenAudioCollection{get;set;}
    public Audio ChatAudio 
    { 
      get{return HiddenAudioCollection.FirstOrDefault();} 
      set
      {
        if(HiddenAudioCollection==null)
          HiddenAudioCollection=new List<Audio>();
        if(HiddenAudioCollection.Any())
        {
          //decide if you throw an exception or delete the existing Audio
        }

        HiddenAudioCollection.Add(value);
      } 
    } 
    //Do the same with ChatVideo
} 
...