简体   繁体   English

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

[英]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成为必需(非空),而无需将MessageRoleRole中的 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)。

  1. 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 ,除非您应用其他选项之一。

  2. 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) - 如果有一天您决定重命名AuthorRole.Id但忘记相应地重命名AuthorId ,这将中断。

  3. 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]方法,因为

  • It is simple and descriptive,它简单明了,
  • No effort needed to think of which shadow property name to use,无需考虑使用哪个影子属性名称,
  • No risk of breaking the shadow property name later on.以后没有破坏影子属性名称的风险。

This may apply sometimes, not always:这有时可能适用,但并非总是如此:

  • Input forms may use the Model class attribute to check if a property is required.输入 forms 可以使用 Model class 属性来检查是否需要属性。 However it may be a better approach to build your forms around DTO classes , and then an attribute on an EF Model class may provide no worth for your forms.然而,围绕DTO 类构建 forms 可能是一种更好的方法,然后在EF Model class上的属性可能对您的 ZAC68B68AB61DZ 没有任何价值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM