73

Consider two classes.

public class File
{
    [Key]
    public string Id { get; set; }

    public string Message_Id { get; set; }

    internal Message Message { get; set; }
}

public class Message 
{
    [Key]
    public string Id { get; set; }       
}

In EF6, for N : 1..0 relation there was this fluent API.

modelBuilder.Entity<File>()
            .HasOptional(e => e.Message ).WithMany().HasForeignKey(e => e.Message_Id);

What is equivalent in Entiity Framework Core 1?

Thank you

Chatumbabub
  • 1,557
  • 2
  • 18
  • 30

3 Answers3

120

You will not find an equivalent method in EF 7. By convention, a property whose CLR type can contain null will be configured as optional. So what decide if the relationship is optional or not is if the FK property is nullable or not respectively.

In summary, due to your Message_Id FK property is string, it already accepts null value, so if you use the following Fluent Api configuration:

modelBuilder.Entity<File>()
            .HasOne(s => s.Message)
            .WithMany()
            .HasForeignKey(e => e.Message_Id)

EF will configure your relationship as optional (or N : 0..1 as requested).

In case of your FK property is value type like int, you should declare it as nullable (int?).

Also I noticed now you have a navigation property with internal access modifier. You should always declare your entity properties as public.

shannon
  • 8,664
  • 5
  • 44
  • 74
ocuenca
  • 38,548
  • 11
  • 89
  • 102
  • Just to add an observation: EF will also configure the relationship as required given a `[Required]` annotation on the property, even if the type itself is nullable. – davidmdem Aug 16 '17 at 14:15
  • Don't think this is true. EF6 AND EF Core are both capable of representing 1 : 0..1 relationships as EntityA.PK is foreign key to EntityB.PK – shannon Sep 06 '17 at 17:38
  • @shannon Take a look in this [link](https://learn.microsoft.com/en-us/ef/core/modeling/relationships) and show me in fluent api how to do an `HasOptional` without do what I explain in my answer – ocuenca Sep 06 '17 at 17:43
  • @shannon, also I never said is not capable of do it. What I show is how to do it in EF Core, because HasOptional method was deprecated. But definitely is possible if the FK property is nullable – ocuenca Sep 06 '17 at 17:56
  • @octavioccl: You are correct, I misread the requirement as "1 : 0..1" (as can be seen in my comment above), when it is clearly stated as "N : 0..1". I'll remove the downvote (will require edit to permit). I would recommend a less personal reaction in the future; I might have been quicker to spot the error was mine if our interaction was less emotional. – shannon Sep 06 '17 at 18:26
  • 1
    My apologies for that. I will take your advice in the future – ocuenca Sep 06 '17 at 18:29
  • 4
    `You should always declare your entity properties as public.` This is incorrect. If you have something you don't want exposed outside of your class, you can declare it as `protected internal` so that Entity Framework can still work with the property, but it can't be accessed by anything else. – krillgar Oct 08 '18 at 12:27
  • Working on EF core after a long time and was trying to sort this out. That nullable ef really helped – Mudasar Rauf Feb 03 '19 at 01:38
  • If your child model doesn't have a foreign key property, use ".IsRequired(bool)" which defaults to true. – AndyClaw May 10 '20 at 15:58
  • If your child model doesn't have a foreign key property, the foreign key field in the database is nullable by default. – AndyClaw May 11 '20 at 15:15
  • 1
    Does HasOne() is the equivalent for HasOptional()? – Karthic G Jan 05 '21 at 05:51
  • @KarthicG only if property is nullable – Defkon1 Mar 29 '21 at 10:15
  • 3
    I found that using `int?` didn't work for me (Net 6.0, EF Core 7.0) as it always created a non-nullable field despite the use of a nullable int. I had to use `builder.Property(p => p.FieldName).IsRequired(false)` when configuring the entity in order to force it. Then it worked. – Mark Dec 18 '22 at 21:52
16

To add to the accepted answer, if your property can't be configured as nullable (for example, if you are dealing with a split-table scenario, where the property is the primary key), adding IsRequired(false), will make the join optional (LEFT).

Like this:

modelBuilder.Entity<File>()
    .HasOne(s => s.Message)
    .WithMany()
    .HasForeignKey(e => e.Message_Id)
    .IsRequired(false)
pkExec
  • 1,752
  • 1
  • 20
  • 39
  • Thank you! I needed this so that I could use a Primary Key as a Foreign Key for a 0..1 relationship. I couldn't make the primary key nullable so this was the solution I needed. – djalonso Sep 01 '22 at 22:22
  • Thank you. This should be the answer since it is simple and easy to understand. – Thomas.Benz Oct 23 '22 at 15:15
12

In EF Core you can use two ways for relating two tables:

  • Inside OnModelCreating:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);            
    
        modelBuilder.Entity<File>()
                    .HasOne(c => c.Message)
                    .WithOne()
                    .HasForeignKey(c => c.MessageId)                           
    }
    
  • Create new class FileConfiguration and calling it inside OnModelCreating:

    public class FileConfiguration : IEntityTypeConfiguration<File>
    {
        public void Configure(EntityTypeBuilder<File> builder)
        {           
            builder.ToTable("File");            
    
            // Id
            builder.HasKey(c => c.Id);
            builder.Property(c => c.Id)
                   .ValueGeneratedOnAdd();
    
            // Message
            builder.HasOne(c => c.Message)
                   .WithOne(c => c.File)
                   .HasForeignKey<Message>(c => c.MessageId)
                   .OnDelete(DeleteBehavior.Restrict);
        }
    }
    

    and inside OnModelCreating put below codes:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    
        modelBuilder.ApplyConfiguration(new FileConfiguration());                                       
    }
    
Sina Lotfi
  • 3,044
  • 1
  • 23
  • 30