Search code examples
c#entity-framework-coreef-core-3.0

EF Core 3.0 : Multiple navigation properties in an entity


Here are the 2 entities I have defined and I am using EF Core 3.0. Whenever I try to create a new migration, I keep getting an error saying can not have the same navigation property in multiple relationships.

Me being a noob in EF Core, any pointers in resolving this issue would be very helpful.

public class Event
    {
        public Guid Id { get; set; }
        public EventContext StartContext { get; set; }
        
        public EventContext EndContext { get; set; } 
        public Guid CreatedBy { get; set; }
        public Guid UpdatedBy { get; set; }
        
    }

    public class EventContext
    {
        public Guid Id { get; set; }
        public Guid EventId { get; set; } //fk
        public Event Event { get; set; }
        public DateTime ApplicableDate { get; set; }        
    }

Solution

  • As the EF Core tools will tell you:

    Cannot create a relationship between 'Event.EndContext' and 'EventContext.Event', because there already is a relationship between 'Event.StartContext' and 'EventContext.Event'. Navigation properties can only participate in a single relationship.
    

    There are two simple solutions to this issue:

    Use two navigation properties on the EventContext type

    If you actually need the navigation property on EventContext and not just on Event, then the following will work:

    public class Event
    {
        public Guid Id { get; set; }
        public Guid CreatedBy { get; set; }
        public Guid UpdatedBy { get; set; }
        
        public EventContext StartContext { get; set; }
        public EventContext EndContext { get; set; }
    }
    
    public class EventContext
    {
        public Guid Id { get; set; }
        public Guid EventId { get; set; }
        public DateTime ApplicableDate { get; set; }
        
        // One navigation property for each corresponding navigation property on `Event`.
        public Event StartContextEvent { get; set; }
        public Event EndContextEvent { get; set; }
    }
    
    public class Context : DbContext
    {
        public DbSet<Event> Events { get; set; }
        public DbSet<EventContext> EventContexts { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Event>(
                entity =>
                {
                    entity.HasOne(e => e.StartContext)
                        .WithOne(e => e.StartContextEvent)
                        .HasForeignKey<EventContext>(e => e.EventId);
    
                    entity.HasOne(e => e.EndContext)
                        .WithOne(e => e.EndContextEvent)
                        .HasForeignKey<EventContext>(e => e.EventId);
                });
        }
    }
    

    Use a one-way navigation property

    If you can live without the navigation property on EventContext and are happy to just use the one on Event, then just remove it from EventContext and the following will work for you as well:

    public class Event
    {
        public Guid Id { get; set; }
        public Guid CreatedBy { get; set; }
        public Guid UpdatedBy { get; set; }
        
        public EventContext StartContext { get; set; }
        public EventContext EndContext { get; set; }
    }
    
    public class EventContext
    {
        public Guid Id { get; set; }
        public Guid EventId { get; set; }
        public DateTime ApplicableDate { get; set; }
    }
    
    public class Context : DbContext
    {
        public DbSet<Event> Events { get; set; }
        public DbSet<EventContext> EventContexts { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Event>(
                entity =>
                {
                    entity.HasOne(e => e.StartContext)
                        .WithOne()
                        .HasForeignKey<EventContext>(e => e.EventId);
    
                    entity.HasOne(e => e.EndContext)
                        .WithOne()
                        .HasForeignKey<EventContext>(e => e.EventId);
                });
        }
    }