Honorable Chow
Honorable Chow

Reputation: 3153

How to map two properties in one code first object to the same parent type

I've been at this for hours and have tried many suggestions I found searching but no luck. I'm using code first EF 5.

The situation is that I have a class Employee. Then I have another class that has two properties on it, both are of type Employee. I want these both to be foreign key constraints but the requirements allow many of the same requests to and from the same users so I can't just use them as keys. I don't really care about Employee having the two collections for navigation but in my working through the problem that seemed a requirement. If it simplifies the problem I can remove those.

I get this message. System.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'Employee_RequestsForEmployee_Target' in relationship 'Employee_RequestsForEmployee'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.

I've tried this using the Fluent API in the OnModelCreation method of the context;

modelBuilder.Entity() .HasRequired(u => u.ForEmployee) .WithMany() .HasForeignKey(u => u.ForEmployeeId);

        modelBuilder.Entity<RevenueTransferRequest>()
                    .HasRequired(u => u.FromEmployee)
                    .WithMany()
                    .HasForeignKey(u => u.FromEmployeeId);

The classes in conflict are (I've removed some properties for clarity);

    public class Employee : IEmployee
    {
        [Key]
        public string Id { get; set; }

        [InverseProperty("ForEmployee")]
        public ICollection<RevenueTransferRequest> RequestsForEmployee { get; set; }

        [InverseProperty("FromEmployee")]
        public ICollection<RevenueTransferRequest> RequestsFromEmployee { get; set; }
    }

 public class RevenueTransferRequest : IRevenueTransferRequest
    {
        [Key]
        public Guid Id { get; set; }

        [Required]
        [ForeignKey("ForEmployee")]
        public String ForEmployeeId { get; set; }

        [InverseProperty("RequestsForEmployee")]
        public Employee ForEmployee { get; set; }

        [Required]
        [ForeignKey("FromEmployee")]
        public String FromEmployeeId { get; set; }

        [InverseProperty("RequestsFromEmployee")]
        public Employee FromEmployee { get; set; }
    }

Any help would be much appreciated. Thanks in advance.

Upvotes: 3

Views: 2485

Answers (1)

Honorable Chow
Honorable Chow

Reputation: 3153

I never did figure out how to do it using data annotations but using the Fluent API I was able to do it. What I was missing was that I had to specify in the HasMany() method what the relationship on the other side was which I assumed was understood through the data annotations and conventions.

This is called in the DbContext OnModelCreating override (The WillCascadeOnDelete(false) is related to another issue).

    modelBuilder.Entity<RevenueTransferRequest>()
                .HasRequired(e => e.FromEmployee)
                .WithMany(x=>x.RequestsFromEmployee)
                .WillCascadeOnDelete(false);

    modelBuilder.Entity<RevenueTransferRequest>()
              .HasRequired(e => e.ForEmployee)
              .WithMany(x => x.RequestsForEmployee)
              .WillCascadeOnDelete(false);

With the classes:

[Key]
        public String Id { get; set; }

        public String BusinessUnitLeaderId { get; set; }

        public Employee BusinessUnitLeader { get; set; }

        [Required]
        [MaxLength(150)]
        public String DisplayName { get; set; }

        public ICollection<Project> BusinessUnitLeaderProjects { get; set; }

        public ICollection<RevenueTransferRequest> RequestsForEmployee { get; set; }

        public ICollection<RevenueTransferRequest> RequestsFromEmployee { get; set; } 

public class RevenueTransferRequest
    {
        [Key]
        public Guid Id { get; set; }

        [Required]
        public String ForEmployeeId { get; set; }

        public Employee ForEmployee { get; set; }

        [Required]
        public String FromEmployeeId { get; set; }

        public Employee FromEmployee { get; set; }

        [Required]
        public String ProjectId { get; set; }

        public Project Project { get; set; }

        [Required]
        public Double? TransferAmount { get; set; }

        public int WorkflowState { get; set; }
    }

Upvotes: 3

Related Questions