lexeme
lexeme

Reputation: 2973

Mapping an entity with multiple references to the same type

For example Project has references (1 optional, 1 required) to the User:

public class Project {
    public User RequiredUser { get; set; }
    public User OptionalUser { get; set; }
}

whereas User has many projects:

public class User {
    public ICollection<Project> Projects { get; set; }
}

Would it be right to configure it like that:

modelBuilder.Entity<User>()
    .HasMany(i => i.Projects)
    .WithRequired(i => i.RequiredUser);

modelBuilder.Entity<Project>().HasOptional(i => i.OptionalUser);

Upvotes: 2

Views: 109

Answers (3)

Christoph Fink
Christoph Fink

Reputation: 23093

I think you code would work, but not they way you expect it to:
ICollection<Project> Projects will only contain Project instances where the User is the RequiredUser.

I personally would use an intermediate table which has a IsRequired flag to map these two entities, but that changes the behaviour a little and introduces a not so easy to handle many-to-many relationship:

public class Project {
    public ICollection<ProjectUser> Users { get; set; }
}

public class ProjectUser {
    public Project Project { get; set; }
    public User User { get; set; }
    public bool IsRequired { get; set; }
}

public class User {
    public ICollection<ProjectUser> Projects { get; set; }
}

The advantage is, that you can have more "optional users" (or required) in one project, but if your requirement is to only have on (required) user you would need to take care of this contraint in your business logic. You could also replace the bool with an enum if you have more then two possible "mapping types", e.g. Owner, Developer and Tester.

NOTE: If you have no use for this advantage you are better of with Flater's answer.

Upvotes: 1

Flater
Flater

Reputation: 13763

From experience, if you set up separate relations (RequiredUser / OptionalUser), you'l also need separate IEnumerable<Project> properties in your User class.

public class User {
    public ICollection<Project> Projects_Required { get; set; }
    public ICollection<Project> Projects_Optional { get; set; }
}

Your setup then becomes:

modelBuilder.Entity<User>()
    .HasMany(i => i.Projects_Required)
    .WithRequired(i => i.RequiredUser);

 modelBuilder.Entity<User>()
    .HasMany(i => i.Projects_Optional)
    .WithOptional(i => i.OptionalUser);

You could then add a AllProjects custom property that combines the two lists, if you so choose. However, it seems a dangerous thing to do, as you need to keep these lists separated because they address separate relations. To avoid confusion, I'd keep them separated as much as possible.

If there is a way to set up EF to have both relations end up in the same List property, I haven't found it. It seems hard and dangerous to implement.

Upvotes: 2

Kangkan
Kangkan

Reputation: 15571

This seems to be a case of circular reference (http://en.wikipedia.org/wiki/Circular_reference) and definitely become a bad design. Think of the design why you need to refer to a User in Project which is already inside a User. Look more here: http://blogs.msdn.com/b/nickmalik/archive/2005/03/18/398601.aspx

Upvotes: 0

Related Questions