EF Core 中是否有可能需要单向导航 属性?

Is it possible in EF Core to make a one-way navigation property required?

我正在开发一个基本的群聊系统,为此我创建了这些 类:

public class Role
{
    public Guid Id { get; set; };
    public string Username { get; set; }
}

public class Message
{
    public Guid Id { get; set; };
    public Role Author { get; set; }
    public Conversation Conversation { get; set; }
    public DateTime Date { get; set; }
    public string Text { get; set; }
}

public class Conversation
{
    public Guid Id { get; set; };
    public IList<ConversationParticipant> ConversationParticipants { get; set; };
    public IList<Message> Messages { get; set; };
}

public class ConversationParticipant
{
    public Conversation Conversation { get; set; }
    public Role Role { get; set; }
}

我们使用 EF Core 3.1 Code-First 进行迁移。

我正在寻找一种方法使 Message.Author 成为 必需的 属性,这应该会导致table Message 中创建为 AuthorId NOT NULL.

的列

我试过了:

public static void Map(this EntityTypeBuilder<Message> builder)
{
    builder.HasOne(m => m.Author);
}

由于这是使用 Add-Migration 和 Update-Database 应用的,因此创建了数据库列 AuthorId,但允许 NULLs。

好像没有方法IsRequired()可以添加到HasOne()之后。

我也试过:

public static void Map(this EntityTypeBuilder<Message> builder)
{
    builder.Property(m => m.Author).IsRequired();
}

但这并没有说明

The property 'Message.Author' is of type 'Role' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

先做 .HasOne(...) 再做 .Property(...).IsRequired() 也不行:

'Author' cannot be used as a property on entity type 'Message' because it is configured as a navigation.

我通过这个设法使Message.Conversation成为必需的:

public static void Map(this EntityTypeBuilder<Conversation> builder)
{
    builder.HasMany(c => c.Messages)       // A conversation can have many messages
           .WithOne(e => e.Conversation)   // Each message belongs to at most 1 conversation
           .IsRequired();                  // A message always has a conversation
}

但是我不想让 Role 知道消息,因为我永远不想直接从角色中检索消息(这将通过对话和参与者发生)。

我的最终问题是:有没有办法使 Message.Author 成为必需的(NOT NULL),而无需将 MessageRole 链接在一起形成完整的一对多关系Role?

中的消息 属性

如何将 Role 的外键添加到 Message,然后要求 属性 不为空?类似于:

// MessageConfiguration.cs
builder.Property(b => b.RoleId).IsRequired()

虽然@Ben Sampica 的回答很有帮助,让我到达了我需要去的地方,但@Ivan Stoev 的评论提供了细节和清晰度,让我认为更全面的答案会很有用。

在生成的 table.

中有多种方法可以使外键列成为必需的(NOT NULL)
  1. 最简单的就是把[Required]放在导航上属性:

    public class Message
    {
        // ...
        [Required] public Role Author { get; set; }
        // ...
    }
    

    这将导致 EF 创建 Guid 类型的影子 属性 AuthorId,因为 Message.Author 是一个 Role 而 Role.Id 是 Guid 类型。在 SQL 服务器的情况下,这会导致 UNIQUEIDENTIFIER NOT NULL

    如果您省略 [Required],那么 EF 将使用 Guid?,这将导致 UNIQUEIDENTIFIER NULL,除非您应用其他选项之一。

  2. 您可以使用类型不能为空的显式 ID 属性:

    public class Message
    {
        // ...
        public Guid AuthorId { get; set; }
        public Role Author { get; set; }
        // ...
    }
    

    注意 (i) - 这仅在您遵循 EF Core shadow property naming rules 时有效,在这种情况下意味着您必须将 Id 命名为 属性 nameof(Author) + nameof(Role.Id) == AuthorId.
    注意 (ii) - 如果有一天您决定重命名 AuthorRole.Id 但忘记相应地重命名 AuthorId,这将会中断。

  3. 如果您不能或不想更改模型 class,那么您可以告诉 EF Core 它需要根据需要处理影子 属性 :

    builder.Property("AuthorId").IsRequired();
    

    相同的 注释 适用于 2 中列出的内容,此外您现在可以使用 nameof() 来减少努力和风险。


最后我决定使用[Required]的方法,因为

  • 简单明了,
  • 无需考虑使用哪个影子 属性 名称,
  • 以后没有打破阴影 属性 名称的风险。

这有时可能适用,但并非总是如此:

  • 输入表单可以使用模型 class 属性来检查是否需要 属性。然而,围绕 DTO classes 构建表单可能是更好的方法,然后在 EF 模型 class[= 上构建属性73=] 可能不会为您的表单提供任何价值。