如何使用实体框架在FluentAPI /数据注释中定义外键可选关系?

Wil*_*Wil 22 c# ef-code-first entity-framework-4.1

我有一个(示例)应用程序,代码如下:

public class Posts
{

    [Key]
    [Required]
    public int ID { get; set; }

    [Required]
    public string TypeOfPost { get; set; }

    public int PollID { get; set; }
    public virtual Poll Poll { get; set; }

    public int PostID { get; set; }
    public virtual Post Post { get; set; }

}
Run Code Online (Sandbox Code Playgroud)

基本上,我不知道是否有更好的方法这样做,但是,我有一个帖子列表,并且,人们可以选择它是a Poll还是a Post,As Entity Framework不能与Enums一起工作,我只是将它存储为字符串TypeOfPost然后在应用程序中,我以编程方式查询Poll或Post的值TypeOfPost.

我认为无论如何设置"只需要一个"或类似,所以,我处理应用程序中的所有检查和内容.(如果有人知道更好的方法,请说出来!).

无论如何,问题是,通过进入SQL Management Studio并手动编辑模式以允许空值,我可以正常工作 - 但是,我只是无法弄清楚如何在FluentAPI中执行此操作,并需要一些帮助.

我尝试了以下两种方法:

modelBuilder.Entity<Post>()
    .HasOptional(x => x.Poll).WithOptionalDependent();

modelBuilder.Entity<Post>()
    .HasOptional(x => x.Poll).WithOptionalPrincipal();
Run Code Online (Sandbox Code Playgroud)

第一个似乎在数据库中创建了一个允许空值的附加列,而第二个列似乎没有做任何事情.

我相信第一个是我需要的,但是,我需要在Post类中与[ForeignKey]结合使用它.如果我在这里是正确的,那么[ForeignKey]应该是虚拟财产还是财产的ID?

另外,WithOptionalDependent和之间的实际区别是WithOptionalPrincipal什么? - 我在MSDN上看过,但是,我真的不明白它的区别.

Wil*_*Wil 18

之所以不允许空值,原因如下:

public int PollID { get; set; }
public virtual Poll Poll { get; set; }

public int PostID { get; set; }
public virtual Post Post { get; set; }
Run Code Online (Sandbox Code Playgroud)

本来应该

public int? PollID { get; set; }
public virtual Poll Poll { get; set; }

public int? PostID { get; set; }
public virtual Post Post { get; set; }
Run Code Online (Sandbox Code Playgroud)


Sla*_*uma 15

我可能会尝试创建两个一对一的关系作为可选:必需因为Poll 必须有一个引用,Posts并且Post必须有一个引用Posts:

modelBuilder.Entity<Posts>()
    .HasOptional(x => x.Post)
    .WithRequired();

modelBuilder.Entity<Posts>()
    .HasOptional(x => x.Poll)
    .WithRequired();
Run Code Online (Sandbox Code Playgroud)

这会Posts自动成为关系和/ PostPoll从属关系中的主体.主体在关系中具有主键,依赖外键,它也是Post/ Polltable中同时的主键,因为它是一对一的关系.只有在一对多关系中,您才能拥有外键的单独列.对于一对一关系,您还必须删除外键列PostId,PollId因为Posts它通过其主键引用PostPoll.

在您的模型中似乎合适的替代方法是继承映射.然后模型看起来像这样:

public abstract class BasePost  // your former Posts class
{
    public int ID { get; set; }
    public string UserName { get; set; }
}

public class Post : BasePost
{
    public string Text { get; set; }
    // other properties of the Post class
}

public class Poll : BasePost
{
    // properties of the Poll class
}
Run Code Online (Sandbox Code Playgroud)

您不再需要TypeOfPost那么,因为您可以使用OfTypeLINQ运算符过滤两种具体类型,例如:

var x = context.BasePosts.OfType<Post>()
    .Where(p => p.UserName == "Jim")
    .ToList();
Run Code Online (Sandbox Code Playgroud)

这将选择特定用户的所有帖子,但不会选择民意调查.

您必须决定要使用哪种继承映射--TPH,TPT或TPC.

编辑

要获得一对多关系,您可以在Fluent API中指定以下映射:

modelBuilder.Entity<Posts>()
    .HasOptional(x => x.Post)
    .WithMany()
    .HasForeignKey(x => x.PostID);

modelBuilder.Entity<Posts>()
    .HasOptional(x => x.Poll)
    .WithMany()
    .HasForeignKey(x => x.PollID);
Run Code Online (Sandbox Code Playgroud)

int?正如您已经找到的那样,外键属性必须为nullable().因为外键属性的命名遵循EF用于映射的命名约定,所以可以完全省略Fluent映射.只有你有非常规名称(如PostFK或类似的东西)才需要它.然后,您还可以使用数据注释([ForeignKey(...)]属性)而不是Fluent API.


Chr*_*ini 8

ForeignKey必须是Nullable才能使其成为可选项 - 虚拟是独立的,只有延迟加载才需要.

声明性EF代码优先的必要关系:

public User User { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
Run Code Online (Sandbox Code Playgroud)

声明性EF Code First中的可选关系:

public User User { get; set; }
[ForeignKey("User")]
public int? UserId { get; set; }
Run Code Online (Sandbox Code Playgroud)

你跑步时会看到它update-database -verbose -f:

ALTER TABLE [dbo].[MyTable] ALTER COLUMN [UserId] [int] NULL
Run Code Online (Sandbox Code Playgroud)