如何解决在EF Core中是否可能需要单向导航属性?
我正在开发一个基本的群聊系统,为此我创建了这些类:
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
成为必需属性的方法,该属性应导致在表Message
中创建的一列为AuthorId NOT NULL
。
我尝试过:
public static void Map(this EntityTypeBuilder<Message> builder)
{
builder.HasOne(m => m.Author);
}
由于这是使用“添加迁移”和“更新数据库”应用的,因此将创建数据库列AuthorId
,但允许NULL
。
似乎没有可以在Isrequired()
之后添加的方法HasOne()
。
我也尝试过:
public static void Map(this EntityTypeBuilder<Message> builder)
{
builder.Property(m => m.Author).Isrequired();
}
但这没说
属性“ Message.Author”的类型为“角色”,当前数据库提供程序不支持。
要么更改属性CLR类型,要么使用“ [NotMapped]”属性或在“ OnModelCreating”中使用“ EntityTypeBuilder.Ignore”忽略该属性。
先进行.HasOne(...)
后再进行.Property(...).Isrequired()
也是行不通的:
我设法通过此操作使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
为必需(不为NULL),而无需将Message
和Role
链接成一个完整的一对多关系Role
中的Messages属性?
解决方法
如何将Role
的外键添加到Message
,然后要求该属性不为null?像这样:
// MessageConfiguration.cs
builder.Property(b => b.RoleId).IsRequired()
,
虽然@Ben Sampica的回答很有帮助,并让我找到了需要的地方,但@Ivan Stoev的评论却提供了细节和清晰度,使我认为更全面的回答将是有用的。
有多种方法可以在生成的表中使外键列成为必需(NOT NULL)。
-
最简单的方法是将
[Required]
放在导航属性上:public class Message { // ... [Required] public Role Author { get; set; } // ... }
这将导致EF创建
AuthorId
类型的阴影属性Guid
,因为Message.Author是Role角色,而Role.Id是Guid类型。对于SQL Server,这将导致UNIQUEIDENTIFIER NOT NULL
。
如果您省略[Required]
,那么EF将使用Guid?
,这将导致UNIQUEIDENTIFIER NULL
,除非您应用其他选项之一。 -
您可以使用类型不能为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)
== {{1 }}。
注释(ii)-如果有一天您决定重命名AuthorId
或Author
但忘记相应地重命名Role.Id
,则此操作将中断。 -
如果您不能或不想更改Model类,则可以告诉EF Core它需要根据需要处理shadow属性:
AuthorId
2 中列出的相同的注释适用,此外,您现在可以使用
builder.Property("AuthorId").IsRequired();
来减少工作量和降低风险。
最后,我决定使用nameof()
方法,因为
- 它简单而具有描述性,
- 无需考虑要使用哪个阴影属性名称,
- 以后没有打破影子属性名称的风险。
这有时可能适用,但并不总是如此:
- 输入表单可以使用Model类属性来检查是否需要属性。但是,用 DTO类构建表单可能是一种更好的方法,然后 EF Model类上的属性可能对您的表单毫无价值。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。