0

I have the following entities: Device and Printer

public class Device
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int DeviceId { get; set; }

    public int? DefaultPrinterId { get; set; }
    [ForeignKey("DefaultPrinterId")]
    public virtual Printer DefaultPrinter { get; set; }
}

public class Printer
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int PrinterId { get; set; }

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

There are 2 relationship between the Device and the Printer

  • Each Printer may or may not be hosted on a specific device, this is represented by the Device foreign key on the Printer entity. This is a one to many relationship.
  • Each Device will be configured to use a particular printer by default. This is represented by the DefaultPrinter foreign key. This is a one to many relationship.

When I generate the database with Entity Framework, I get the error: "unable to determine the principal end of an association" It's not too hard to find information about how this error relates to a 1-to-1 relationships, but I haven't found anything about how this relates to two one to many relationships.

Is there any way to tell EF that I'm not trying to define a 1-to-1 relationship?

  • I don't think you can get that done in EF without a bridge table since Device and Printer would need to share a key (http://stackoverflow.com/questions/6531671/what-does-principal-end-of-an-association-means-in-11-relationship-in-entity-fr). I think you will need a collection of Printers in Device and a collection of Devices in Printer. – Steve Greene Jan 08 '16 at 18:38

1 Answers1

2

The problem is that you are not specifying the "end" of the relationship. Because the both relationships are not 1-1, but 1-n.

A Devicecan have a DefaultPrinter, which means that Printer can be the "Default Printer" of many devices.

It can be easily solved by using Fluent API (remove the existing [ForeignKey] attributes). Like this:

modelBuilder.Entity<Printer>()
    .HasOptional(i => i.Device)
    .WithMany() //we don't have navigation property on the other side
    .HasForeignKey(i => i.DeviceId);

modelBuilder.Entity<Device>()
    .HasOptional(i => i.DefaultPrinter)
    .WithMany() //we don't have navigation property on the other side
    .HasForeignKey(i => i.DefaultPrinterId);

Let's imagine that you want to know which devices have a specific printer as its "default printer".

public class Device
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int DeviceId { get; set; }

    public int? DefaultPrinterId { get; set; }

    public virtual Printer DefaultPrinter { get; set; }

    //that's new
    public virtual ICollection<Printer> PrintersThatLoveMe { get; set; }
}

public class Printer
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int PrinterId { get; set; }

    public int? DeviceId { get; set; }

    public virtual Device Device { get; set; }

    //that's new
    public virtual ICollection<Device> DevicesThatLoveMe { get; set; }

}

Mapping:

modelBuilder.Entity<Printer>()
    .HasOptional(i => i.Device)
    .WithMany(i => i.PrintesThatLoveMe)
    .HasForeignKey(i => i.DeviceId);

modelBuilder.Entity<Device>()
    .HasOptional(i => i.DefaultPrinter)
    .WithMany(i => i.DevicesThatLoveMe)
    .HasForeignKey(i => i.DefaultPrinterId);

Hope it helps!

Fabio
  • 11,892
  • 1
  • 25
  • 41