5

First off, I know there are a lot of posts about the Unable to determine the principal end of an association between the types error but ever single one I see does not match my issue, if I missed one sorry about that.

I have built an Entity that will end up referencing it's self twice and when I put the code in for the first self reference it works fine, as soon as ad the code for the second it breaks. Doing some testing I have found that if I use either of the self references by them self everything works fine, it is only when I add the second self reference that it breaks. The code I am using for the self references is:

    [ForeignKey("ManagerID")]
    public User Manager { get; set; }

    //Auditing Fields
    public DateTime DateCreated { get; set; }
    public int? UpdatedByUserID { get; set; }
    public DateTime? DateUpdated { get; set; }
    public DateTime LastAutoUpdate { get; set; }

    [ForeignKey("UpdatedByUserID")]
    public User UpdatedByUser { get; set; }

The full entity code block is:

public class User
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public int ADPFileNumber { get; set; }

    [Required]
    public string ADUserName { get; set; }

    public int AirCardCheckInLateCount { get; set; }

    [Required]
    public string Email { get; set; }

    public DateTime? EndDate { get; set; }

    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    public int ManagerID { get; set; }
    public string MobilePhone { get; set; }

    [Required]
    public string Office { get; set; }

    [Required]
    public string Phone { get; set; }

    public decimal PTO { get; set; }
    public DateTime StartDate { get; set; }
    public int VehicleCheckInLateCount { get; set; }
    public int WexCardDriverID { get; set; }

    [ForeignKey("ManagerID")]
    public User Manager { get; set; }

    //Auditing Fields
    public DateTime DateCreated { get; set; }
    public int? UpdatedByUserID { get; set; }
    public DateTime? DateUpdated { get; set; }
    public DateTime LastAutoUpdate { get; set; }

    [ForeignKey("UpdatedByUserID")]
    public User UpdatedByUser { get; set; }
}

What am I missing that cause the second self reference to break?

Matthew Verstraete
  • 6,335
  • 22
  • 67
  • 123

2 Answers2

2

You have to indicate the principal end of both associations explicitly. You can do that with the class you had originally, without inverse collection properties:

modelBuilder.Entity<User>()
            .HasOptional(u => u.Manager)
            .WithMany()
            .HasForeignKey(u => u.ManagerID);
modelBuilder.Entity<User>()
            .HasOptional(u => u.UpdatedByUser)
            .WithMany()
            .HasForeignKey(u => u.UpdatedByUserID);

Note that ManagerID should be an int? as well. You can't create any User if it requires another user to preexist. That's a chicken-and-egg problem.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • That seems to have worked and nice catch on the nullable type, was a type-o on my part. Question though, is this the only way to do it? I would rather have the entire entity definition in the class and not split out. – Matthew Verstraete Mar 07 '16 at 22:28
  • You *can* use two inverse collection properties as proposed by Louis (but also using the `[InverseProperty]` attribute). Without these (which you seem to prefer), the only way is to use fluent mapping. – Gert Arnold Mar 07 '16 at 22:34
0

As mentionned in Multiple self-referencing relationships in Entity Framework, you seem to be missing the other part of the relationship.

i.e.

[InverseProperty("Manager")]
public virtual ICollection<User> ManagedUsers {get;set;}
[InverseProperty("UpdatedByUser")]
public virtual ICollection<User> UpdatedUsers {get;set;}

EDIT: based on @Gert Arnold's answer you should indeed add the [InverseProperty] attribute

Community
  • 1
  • 1
Louis
  • 593
  • 4
  • 13
  • Based off your linked question and your answer I updated my code, and while the code now builds it is not creating the second key. It creates the FK for ManagerID but not for UpdatedByUserID – Matthew Verstraete Mar 07 '16 at 21:16