Matthew Flynn
Matthew Flynn

Reputation: 3931

Issue with Database Migrations on Entity Framework 6

I have the following classes (shortened for brevity);

public class InsurancePolicy : AuditableEntity<int>
{
    [Column("id"), Key]
    public override int ID { get; set; }
    [Column("deviceid"), ForeignKey("Device")]
    public int DeviceId { get; set; }
    public virtual Device Device { get; set; }
}

public partial class Device : AuditableEntity<int>
{
    [Column("deviceid"), Key]
    public override int ID { get; set; }

    [ForeignKey("Policy")]
    public int PolicyId { get; set; }
    public virtual InsurancePolicy Policy { get; set; }

    [ForeignKey("Vehicle")]
    public int VehicleId { get; set; }
    public virtual Vehicle Vehicle { get; set; }
}

public partial class Vehicle : AuditableEntity<int>
{
    [Column("id"), Key]
    public override int ID { get; set; }

    [Column("deviceid"), ForeignKey("Device")]
    public int DeviceId { get; set; }
    public virtual Device Device { get; set; }

    public virtual List<InsurancePolicyVehicle> InsurancePolicyVehicles { get; set; }
}

Now when I try and run my update-database command I get this error:

Device_Policy_Target: : Multiplicity is not valid in Role 'Device_Policy_Target' in relationship 'Device_Policy'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.

Device_Vehicle_Target: : Multiplicity is not valid in Role 'Device_Vehicle_Target' in relationship 'Device_Vehicle'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.

Could anyone please advise what I am doing wrong here?

Upvotes: 2

Views: 83

Answers (1)

Jon Egerton
Jon Egerton

Reputation: 41539

Taking the Device_Policy_Target relationship as an example, you have the following (trimming down to the the relevant properties for this relationship):

public class InsurancePolicy : AuditableEntity<int>
{   
    [Column("id"), Key]
    public override int ID { get; set; }
    [Column("deviceid"), ForeignKey("Device")]
    public int DeviceId { get; set; }
    public virtual Device Device { get; set; }
}

public partial class Device : AuditableEntity<int>
{
    [Column("deviceid"), Key]
    public override int ID { get; set; }

    [ForeignKey("Policy")]
    public int PolicyId { get; set; }
    public virtual InsurancePolicy Policy { get; set; }

}

What you're defining here is a one-to-one relationship. EF only supports such relationships where the two tables share the primary key. In your configuration your child table - InsurancePolicy has its own PK, and a Foreign Key to Device. EF only supports one-to-many relationships with these constraints.

To define these relationships to be one-to-one, switch your setup to something along the lines of:

public class InsurancePolicy : AuditableEntity<int>
{
    [Column("deviceid"), Key, ForeignKey("Device")]
    public override int ID { get; set; }
    public virtual Device Device { get; set; }
}

public partial class Device : AuditableEntity<int>
{
    [Column("deviceid"), Key]
    public override int ID { get; set; }

    public virtual InsurancePolicy Policy { get; set; }    
}

This is all partially a consequence of EF not supporting unique constraints. This article has a fuller explanation of one-to-one relationships using DataAnnotations

Update:

To "fake" the one-to-one using the tables/keys you've got then consider something like this:

public class InsurancePolicy : AuditableEntity<int>
{   
    [Column("id"), Key]
    public override int ID { get; set; }
    [Column("deviceid"), ForeignKey("Device")]
    public int DeviceId { get; set; }
    public virtual Device Device { get; set; }
}

public partial class Device : AuditableEntity<int>
{
    [Column("deviceid"), Key]
    public override int ID { get; set; }

    public virtual IEnumerable<InsurancePolicy> PoliciesAsList { get; set; }

    [NotMapped]
    public virtual InsurancePolicy Policy {
        get {
            return (PoliciesAsList != null) 
                ? PoliciesAsList.FirstOrDefault() 
                : null;                
        }
    }

}

In this case I've arbitrarly chosen Device as the parent - it's not important which you choose.

You should also make sure that the database has a unique constraint on deviceid on the InsurancePolicy table.

Upvotes: 1

Related Questions