Search code examples
entity-frameworkentity-framework-coreef-code-firstentity-framework-migrations

Define circular relationship for ICollection of bookings in entity framework


I am trying to add a circular relationship in my project. I have the following problem: My database consists of a table with bookings (on a specific machine). Since the machines can handle multiple bookings at once, I have another table that stores all the (overlapping) parallel bookings. How can I now attach the overlapping bookings to the original booking element? I would like to access the overlaps like this:

var bookings = dbContext.Booking.Include(x => x.OverlapBookings).ToList();
foreach (var booking in bookings)
{
    var overlaps = booking.OverlapBookings;
    ...

However, when trying to add the migration, I am running into the following error:

Unable to determine the relationship represented by navigation 'BookingDbModel.OverlapBookings' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

How can I now define this circular relationship?

Here are the classes:

public class BookingDbModel
{
    public int id { get; set; }
    public string Name { get; set; }
    public string Client { get; set; }
    public string Machine { get; set; }
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; }
    public ICollection<OverlapBookingDbModel> OverlapBookings { get; set; }
}

and

public class OverlapBookingDbModel
{
    public int OriginalBookingId { get; set; }
    public BookingDbModel OriginalBooking { get; set; }
    public int TargetBookingId { get; set; }
    public BookingDbModel TargetBooking { get; set; }
}

Solution

  • With the following manual relationship definition, the entity updated successfully and all the models are now accessible with only one dbContext call:

    DbContext.cs

    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
        {
        }
        public DbSet<BookingDbModel> Booking { get; set; }
        public DbSet<OverlapBookingDbModel> OverlapBooking { get; set; }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<OverlapBookingDbModel>()
                .HasOne(p => p.OriginalBooking)
                .WithMany(b => b.OverlapBookings)
                .HasForeignKey(k => k.OriginalBookingId);
    
            base.OnModelCreating(modelBuilder);
        }
    }
    

    I can now access all related Overlapbookings like this:

    var testbookings = dbContext.Booking.Include(x => x.OverlapBookings).ThenInclude(y => y.TargetBooking).FirstOrDefault(x => x.Id == 12);