EF Code First foreign key without navigation property

Viewed 60922

Let's say I have the following entities:

public class Parent
{
    public int Id { get; set; }
}
public class Child
{
    public int Id { get; set; }
    public int ParentId { get; set; }
}

What is the code first fluent API syntax to enforce that ParentId is created in the database with a foreign key constraint to the Parents table, without the need to have a navigation property?

I know that if I add a navigation property Parent to Child, then I can do this:

modelBuilder.Entity<Child>()
    .HasRequired<Parent>(c => c.Parent)
    .WithMany()
    .HasForeignKey(c => c.ParentId);

But I don't want the navigation property in this particular case.

6 Answers

In case of EF Core you don't necessarily need to provide a navigation property. You can simply provide a Foreign Key on one side of the relationship. A simple example with Fluent API:

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace EFModeling.Configuring.FluentAPI.Samples.Relationships.NoNavigation
{
    class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
             modelBuilder.Entity<Post>()
                .HasOne<Blog>()
                .WithMany()
                .HasForeignKey(p => p.BlogId);
        }
    }

    public class Blog
    {
         public int BlogId { get; set; }
         public string Url { get; set; }
    }

    public class Post
    {
         public int PostId { get; set; }
         public string Title { get; set; }
         public string Content { get; set; }

        public int BlogId { get; set; }
    }
}

I'm using .Net Core 3.1, EntityFramework 3.1.3. I have been searching around and the Solution I came up with was using the generic version of HasForeginKey<DependantEntityType>(e => e.ForeginKeyProperty). you can create a one to one relation like so:

builder.entity<Parent>()
.HasOne<Child>()
.WithOne<>()
.HasForeginKey<Child>(c => c.ParentId);

builder.entity<Child>()
    .Property(c => c.ParentId).IsRequired();

Hope this helps or at least provides some other ideas on how to use the HasForeginKey method.

My reason for not using navigation properties is class dependencies. I separated my models to few assemblies, which can be used or not used in different projects in any combinations. So if I have entity which has nagivation property to class from another assembly, I need to reference that assembly, which I want to avoid (or any project which uses part of that complete data model will carry everything with it).

And I have separate migration app, which is used for migrations (I use automigrations) and initial DB creation. This project references everything by obvious reasons.

Solution is C-style:

  • "copy" file with target class to migration project via link (drag-n-drop with alt key in VS)
  • disable nagivation property (and FK attribute) via #if _MIGRATION
  • set that preprocessor definition in migration app and don't set in model project, so it will not reference anything (don't reference assembly with Contact class in example).

Sample:

    public int? ContactId { get; set; }

#if _MIGRATION
    [ForeignKey(nameof(ContactId))]
    public Contact Contact { get; set; }
#endif

Of course you should same way disable using directive and change namespace.

After that all consumers can use that property as usual DB field (and don't reference additional assemblies if they aren't needed), but DB server will know that it is FK and can use cascading. Very dirty solution. But works.

Related