[英]Is it possible in EF Core to make a one-way navigation property required?
I am working on a basic group chat system, for which I created these classes:我正在开发一个基本的群聊系统,为此我创建了这些类:
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; }
}
We are using EF Core 3.1 Code-First with migrations.我们在迁移中使用 EF Core 3.1 Code-First。
I am looking for a way to make Message.Author
a required property , which should lead to a column in table Message
that is created as AuthorId NOT NULL
.我正在寻找一种使
Message.Author
成为必需属性的方法,这应该导致表Message
中的列被创建为AuthorId NOT NULL
。
I tried:我试过了:
public static void Map(this EntityTypeBuilder<Message> builder)
{
builder.HasOne(m => m.Author);
}
As this is applied using Add-Migration and Update-Database, the database column AuthorId
is created, but with NULL
s allowed.由于这是使用 Add-Migration 和 Update-Database 应用的,因此会创建数据库列
AuthorId
,但允许使用NULL
。
There does not seem to be a method IsRequired()
that I can add after HasOne()
.似乎没有我可以在 HasOne() 之后添加的方法
IsRequired()
HasOne()
。
I also tried:我也试过:
public static void Map(this EntityTypeBuilder<Message> builder)
{
builder.Property(m => m.Author).IsRequired();
}
but that fails saying但这没有说
The property 'Message.Author' is of type 'Role' which is not supported by current database provider.
属性“Message.Author”属于“角色”类型,当前数据库提供程序不支持。 Either change the property CLR type or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
更改属性 CLR 类型或使用“[NotMapped]”属性或使用“OnModelCreating”中的“EntityTypeBuilder.Ignore”忽略该属性。
Doing .HasOne(...)
followed by .Property(...).IsRequired()
also does not work:执行
.HasOne(...)
后跟 .Property( .Property(...).IsRequired()
也不起作用:
'Author' cannot be used as a property on entity type 'Message' because it is configured as a navigation.
“作者”不能用作实体类型“消息”的属性,因为它被配置为导航。
I managed to make Message.Conversation
required through this:我设法通过以下方式使
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
}
However I'd rather not make Role
aware of Messages, as I will never want to retrieve Messages directly from a Role (this will happen through Conversations and Participants).但是,我不想让
Role
知道消息,因为我永远不想直接从角色中检索消息(这将通过对话和参与者发生)。
My ultimate question is: Is there a way to make Message.Author
required (NOT NULL), without linking Message
and Role
together in a full 1-to-many relationship with a Messages property in Role
?我的最终问题是:有没有一种方法可以使
Message.Author
成为必需(非空),而无需将Message
和Role
与Role
中的 Messages 属性以完全一对多的关系链接在一起?
What about adding Role
's foreign key to Message
and then requiring that property to not be null?将
Role
的外键添加到Message
然后要求该属性不是 null 怎么样? Something like:就像是:
// MessageConfiguration.cs
builder.Property(b => b.RoleId).IsRequired()
While the answer by @Ben Sampica was helpful and got me where I needed to be, the comments by @Ivan Stoev provided details and clarity that made me think that a more comprehensive answer would be useful.虽然@Ben Sampica 的回答很有帮助并让我到达了我需要的地方,但@Ivan Stoev 的评论提供了细节和清晰度,让我认为更全面的答案会很有用。
There are multiple ways to make a foreign key column required (NOT NULL) in the generated table.有多种方法可以在生成的表中使外键列成为必需(NOT NULL)。
The simplest is to put [Required]
on the navigation property:最简单的是将
[Required]
放在导航属性上:
public class Message { //... [Required] public Role Author { get; set; } //... }
This will cause EF to create a shadow property AuthorId
of type Guid
because Message.Author is a Role and Role.Id is of type Guid.这将导致 EF 创建
Guid
类型的影子属性AuthorId
,因为 Message.Author 是 Role 并且 Role.Id 是 Guid 类型。 This leads to UNIQUEIDENTIFIER NOT NULL
in case of SQL Server.在 SQL 服务器的情况下,这会导致
UNIQUEIDENTIFIER NOT NULL
。
If you omit [Required]
then EF will use Guid?
如果省略
[Required]
则 EF 将使用Guid?
, which leads to UNIQUEIDENTIFIER NULL
, unless you apply one of the other options. ,这会导致
UNIQUEIDENTIFIER NULL
,除非您应用其他选项之一。
You can use an explicit Id property with a type that can't be null:您可以使用类型不能为 null 的显式 Id 属性:
public class Message { //... public Guid AuthorId { get; set; } public Role Author { get; set; } //... }
Note (i) - This only works if you follow EF Core shadow property naming rules , which in this case means you must name the Id property nameof(Author) + nameof(Role.Id)
== AuthorId
.注意 (i) - 这仅在您遵循 EF Core 影子属性命名规则时才有效,在这种情况下,这意味着您必须将 Id 属性命名为
nameof(Author) + nameof(Role.Id)
== AuthorId
。
Note (ii) - This will break if one day you decide to rename Author
or Role.Id
but forget to rename AuthorId
accordingly.注意 (ii) - 如果有一天您决定重命名
Author
或Role.Id
但忘记相应地重命名AuthorId
,这将中断。
If you can't or don't want to change the Model class, then you can tell EF Core that it needs to treat the shadow property as required:如果您不能或不想更改 Model class,那么您可以告诉 EF Core 它需要按要求处理 shadow 属性:
builder.Property("AuthorId").IsRequired();
The same Notes apply as listed at 2 , with the addition that you could now use nameof()
to reduce the effort and the risks.与2中列出的相同的注释适用,此外您现在可以使用
nameof()
来减少工作量和风险。
In the end I decided to use the [Required]
approach, because最后我决定使用
[Required]
方法,因为
This may apply sometimes, not always:这有时可能适用,但并非总是如此:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.