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
,但允许 NULL
s。
好像没有方法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),而无需将 Message
和 Role
链接在一起形成完整的一对多关系Role
?
中的消息 属性
如何将 Role
的外键添加到 Message
,然后要求 属性 不为空?类似于:
// MessageConfiguration.cs
builder.Property(b => b.RoleId).IsRequired()
虽然@Ben Sampica 的回答很有帮助,让我到达了我需要去的地方,但@Ivan Stoev 的评论提供了细节和清晰度,让我认为更全面的答案会很有用。
在生成的 table.
中有多种方法可以使外键列成为必需的(NOT NULL)
最简单的就是把[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
,除非您应用其他选项之一。
您可以使用类型不能为空的显式 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) - 如果有一天您决定重命名 Author
或 Role.Id
但忘记相应地重命名 AuthorId
,这将会中断。
如果您不能或不想更改模型 class,那么您可以告诉 EF Core 它需要根据需要处理影子 属性 :
builder.Property("AuthorId").IsRequired();
相同的 注释 适用于 2 中列出的内容,此外您现在可以使用 nameof()
来减少努力和风险。
最后我决定使用[Required]
的方法,因为
- 简单明了,
- 无需考虑使用哪个影子 属性 名称,
- 以后没有打破阴影 属性 名称的风险。
这有时可能适用,但并非总是如此:
- 输入表单可以使用模型 class 属性来检查是否需要 属性。然而,围绕 DTO classes 构建表单可能是更好的方法,然后在 EF 模型 class[= 上构建属性73=] 可能不会为您的表单提供任何价值。
我正在开发一个基本的群聊系统,为此我创建了这些 类:
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
,但允许 NULL
s。
好像没有方法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),而无需将 Message
和 Role
链接在一起形成完整的一对多关系Role
?
如何将 Role
的外键添加到 Message
,然后要求 属性 不为空?类似于:
// MessageConfiguration.cs
builder.Property(b => b.RoleId).IsRequired()
虽然@Ben Sampica 的回答很有帮助,让我到达了我需要去的地方,但@Ivan Stoev 的评论提供了细节和清晰度,让我认为更全面的答案会很有用。
在生成的 table.
中有多种方法可以使外键列成为必需的(NOT NULL)最简单的就是把
[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
,除非您应用其他选项之一。您可以使用类型不能为空的显式 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) - 如果有一天您决定重命名Author
或Role.Id
但忘记相应地重命名AuthorId
,这将会中断。如果您不能或不想更改模型 class,那么您可以告诉 EF Core 它需要根据需要处理影子 属性 :
builder.Property("AuthorId").IsRequired();
相同的 注释 适用于 2 中列出的内容,此外您现在可以使用
nameof()
来减少努力和风险。
最后我决定使用[Required]
的方法,因为
- 简单明了,
- 无需考虑使用哪个影子 属性 名称,
- 以后没有打破阴影 属性 名称的风险。
这有时可能适用,但并非总是如此:
- 输入表单可以使用模型 class 属性来检查是否需要 属性。然而,围绕 DTO classes 构建表单可能是更好的方法,然后在 EF 模型 class[= 上构建属性73=] 可能不会为您的表单提供任何价值。