【问题标题】:How do I define Foreign Key Optional Relationships in FluentAPI/Data Annotations with the Entity Framework?如何使用实体框架在 FluentAPI/数据注释中定义外键可选关系?
【发布时间】:2011-12-22 15:06:43
【问题描述】:

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

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

}

基本上,我不知道是否有更好的方法来做到这一点,但是,我有一个帖子列表,人们可以选择它是 Poll 还是 Post,作为实体框架不适用于 Enums,我只是将它作为字符串存储在 TypeOfPost 中,然后在应用程序中,我根据 TypeOfPost 的值以编程方式查询 Poll 或 Post。

我不认为有任何设置“只需要一个”或类似的设置,因此,我处理应用程序中的所有检查和内容。 (如果有人知道更好的方法,请说!)。

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

我已经尝试了以下两种方法:

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

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

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

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

另外,WithOptionalDependentWithOptionalPrincipal 的实际区别是什么? - 我在 MSDN 上看过,但是,我真的不明白其中的区别。

【问题讨论】:

  • 这真的是Post 类中的自引用吗?我的意思是:Post 的类和导航属性的类型Post 真的一样吗?你能否也画出Poll 类。
  • @Slauma 抱歉,没有 :( 我只是想输入一段示例代码,但没有看到错误...谢谢。

标签: c# ef-code-first entity-framework-4.1


【解决方案1】:

我可能会尝试将两个一对一的关系创建为 optional:required,因为 Poll 必须 引用 PostsPost必须引用Posts

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

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

这使得Posts 自动成为关系中的主体,而PostPoll 成为依赖关系。主体在关系中有主键,在Post/Poll表中同时也是主键的外键,因为它是一对一的关系。只有在一对多的关系中,您才会有一个单独的外键列。对于一对一的关系,您还必须删除外键列 PostIdPollId,因为 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
}

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

var x = context.BasePosts.OfType<Post>()
    .Where(p => p.UserName == "Jim")
    .ToList();

这将选择特定用户的所有帖子,但不选择投票。

然后您必须决定要使用哪种继承映射 - TPH, TPT or 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);

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

【讨论】:

  • +1 非常感谢您的帮助-但是,替代解决方案目前有点超出我的技能...但是,我找到了解决方法,它在下面的答案中-我感谢您的帮助,如果您可以添加关于 int 的答案?而不是 int,我很乐意将答案切换为您的答案。
  • 感谢您的编辑!很抱歉造成混淆......在实际应用程序中,我有两个单独的表需要引用这个单个表,所以,一对一就行不通(没有第三个表:/),我需要可选的一对多,而且,我只是无法让它工作(显然主要原因是 int 而不是 int?) - 我不相信使用 Int 需要流畅的 API?让它工作......但是,当我试图“掌握”流畅的 API 时,很高兴知道需要什么。非常感谢... +1 / 回答
【解决方案2】:

它不允许空值的原因如下:

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

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

应该是

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

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

【讨论】:

  • 可以为空的属性将创建可选的一对多关系,是的。没关系,也是最简单的方法,但它不是一对一的关系(根本没有单独的外键属性)。您在问题中显示的映射是一对一的关系,我假设您不想要一对多的关系。
  • @Slauma - 我真的不想要一对一的关系 - 我想要一个可选的一对多,(正如我所说,与可选外键的关系),我说 Fluent API 代码我试过没有用,但是,它不是正确的 - 查看从这个类生成的架构,没有任何流畅的 API 是我真正想要的(以及我认为我解释的)。
  • 哦,非常抱歉。我完全误解了。我显然太固定在您尝试的一对一映射上。我已经对我的答案进行了额外的编辑。但基本上可以为空的int? FK 属性就是你所需要的:)
【解决方案3】:

ForeignKey 只需为 Nullable 即可使其成为可选 - virtual 是独立的,仅在延迟加载时需要。

声明性 EF Code First 中的必需关系:

public User User { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }

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

public User User { get; set; }
[ForeignKey("User")]
public int? UserId { get; set; }

你会在运行update-database -verbose -f时看到它:

ALTER TABLE [dbo].[MyTable] ALTER COLUMN [UserId] [int] NULL

【讨论】:

    【解决方案4】:

    其他可能有帮助的东西。即使 FK 属性可以为空,使用 [Required] 属性设置外键属性(注释)也将强制执行 EF 所需的导航属性。我有一个遗留数据的特殊情况,其中需要 FK 属性,但可能会或可能不会引用关系中的记录。有道理,但我不认为 EF 这么“聪明”。

        [Required] <-- even if the FK is nullable, OrgUnit will be Required
        [StringLength(10)]
        [ForeignKey("OrgUnit"), Column(Order = 1)]
        public string OrgCode
        {
            get;
            set;
        }
    
        ... other FK, Column order 0   
    
        public virtual OrgUnit OrgUnit
        {
            get;
            set;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-29
      • 1970-01-01
      • 2017-07-20
      • 2013-11-04
      • 1970-01-01
      • 2018-10-25
      相关资源
      最近更新 更多