EF Code First可选导航

时间:2011-12-12 22:01:24

标签: entity-framework entity-framework-4 entity-framework-4.1 ef-code-first code-first

我有以下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; }
}

DBA具有以下表格设置

-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 个答案:

答案 0 :(得分:2)

不,您无法使用Entity Framework将此模型映射到数据库架构(无论当前版本为4.2的版本)。

主要原因是EF不支持唯一键约束,这意味着:将引用导航属性映射到数据库中具有唯一键约束的列。 EF将认为数据库中的此类FK列始终不是唯一的,并且不会看到唯一键约束。因此,Chat中两个关联的结尾必须是集合。或者您无法在模型中公开它们,但内部EF会将它们视为“很多” - 结束。

EF支持的唯一真正的一对一关系是共享主键关系。这意味着您在ChatIdAudio表中没有Video列,而是主键列AudioIdVideoId是外键同时列到Chat表的列。显然,主键列不能是数据库中所有自动生成的标识,以使这种映射成为可能。

这里可以找到一对一映射策略的优秀描述以及它们的优点和局限性的比较:

答案 1 :(得分:0)

您可以“欺骗”并将关系配置为一对多,在对象模型中强制执行一对一(同样,在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
} 

答案 2 :(得分:0)

让我确保我理解正确:

聊天有零个或一个音频,零个或一个视频。

音频(如果存在)属于1个聊天。 视频(如果存在)属于1个聊天。

这是对的吗?如果是这样,你的DBA会介意这样的架构吗?

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

答案 3 :(得分:0)

另一种选择,因为视频和音频“属于”聊天是使用复杂的结构。不要给视频和音频自己的ID,它们将被映射到聊天表。我知道有些DBA讨厌这个想法,因为它是可选数据。但从技术上讲,它与直接在Customer表中存储地址没有什么不同。这完全取决于您希望数据库的规范化程度。