Search code examples
entity-frameworkef-code-firstef-code-first-mapping

EF: validation error for 1:0..1 relationship in data model with navigation properties


I have this simple data model of some reservations and theirs cancellations:

[Table("ReservationCreation")]
public class ReservationCreation
{
    [Key()]
    public int ReservationCreationId { get; set; }

    [InverseProperty("ReservationCreation")]
    public virtual ReservationCancellation ReservationCancellation { get; set; }
}

[Table("ReservationCancellation")]
public class ReservationCancellation
{
    [Key()]
    [ForeignKey("ReservationCreation")]
    public int ReservationCancellationId { get; set; }

    [Required]
    [ForeignKey("ReservationCancellationId")]
    [InverseProperty("ReservationCancellation")]
    public virtual ReservationCreation ReservationCreation { get; set; }
}

public class DbContext : System.Data.Entity.DbContext
{
    public DbContext() : base(@"DefaultConnection") { }
    public DbSet<ReservationCancellation> ReservationCancellation { get; set; }
    public DbSet<ReservationCreation> ReservationCreation { get; set; }
}

internal sealed class Configuration : DbMigrationsConfiguration<DbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }
}

Here is the code of the test. First the reservation is created and then it is cancelled. When the cancellation record is being saved into database then an exception is thrown "The ReservationCreation field is required". How can I create cancellation record only from the reservation's ID and at the same time have the navigation properties defined?

class Program
{
    static void Main(string[] args)
    {
        int reservationId;

        // create reservation
        using (var db = new DbContext())
        {
            var reservation = 
                db.ReservationCreation.Add(
                    new ReservationCreation());
            db.SaveChanges();
            reservationId = reservation.ReservationCreationId;
        }

        // cancel reservation by its Id
        using (var db = new DbContext())
        {
            var cancellation =
                db.ReservationCancellation.Add(
                    new ReservationCancellation
                    {
                        ReservationCancellationId = reservationId
                    });
            try
            {
                // an exception is thrown
                db.SaveChanges();
            }
            catch(DbEntityValidationException ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
                foreach (var err in ex.EntityValidationErrors.SelectMany(x_ => x_.ValidationErrors))
                    System.Diagnostics.Debug.WriteLine($"!!!ERROR!!! {err.PropertyName}: {err.ErrorMessage}");
            }
        }
    }
}

I did not find any way how to modify the data model annotations. If I remove [Required] from ReservationCreation property then I am not able to create the migration {or connect to the database with that data model).


Solution

  • Your mixing things up in your ReservationCancellation model.

    In your ReservationCreation property you are referring to the primary key entity instead of the ReservationCreation property.

    Try this.

    [Table("ReservationCancellation")]
    public class ReservationCancellation
    {
        [Key()]
        public int ReservationCancellationId { get; set; }
    
        [ForeignKey("ReservationCreation")]
        public int ReservationCreationId { get; set; }
    
        [Required]
        public virtual ReservationCreation ReservationCreation { get; set;   }
    }
    

    Update

    Since you want only one cancellation per creation, you can do this using a simpler model.

    [Table("ReservationCreation")]
    public class ReservationCreation
    {
        [Key()]
        public int ReservationCreationId { get; set; }
    
        public virtual ReservationCancellation ReservationCancellation { get; set; }
    }
    
    [Table("ReservationCancellation")]
    public class ReservationCancellation
    {
        [Key()]
        public int ReservationCancellationId { get; set; }
    
        public virtual ReservationCreation ReservationCreation { get; set; }
    }