0

Having some trouble with relations in Entity Framework.

I found a similar post: Entity Framework Entity w/ One-to-Many and One-to-One?

But it does not answer the question I have.

What I'm trying to accomplish is a relation between two entities which have both a One-To-Many relation AND a One-To-One/Optional.

This is the code:

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

    public string Username { get; set; }

    [Required]
    public int GroupID { get; set; }

    [ForeignKey("GroupID")]
    public virtual Group Group { get; set; }
}

public class Group
{
    [Key]
    public int ID { get; set; }

    public string Name { get; set; }

    [Required]
    public int GroupAdminID { get; set; }

    [ForeignKey("GroupAdminID")]
    public virtual User GroupAdmin { get; set; }        

    [ForeignKey("UserId")]
    public virtual List<User> Members { get; set; }
}

These are linked with this fluent API code:

modelBuilder.Entity<User>().HasRequired(u => u.Group).WithMany(g => g.Members);
modelBuilder.Entity<Group>().HasRequired(g => g.GroupAdmin).WithOptional();

The group can have several members, but only one admin. While a user is connected to one (and only one) group.

I'm clearly messing something up, cause in my head this should work.

If anyone can help I'd appreciate it.

Regards, Robin

Community
  • 1
  • 1
Robin Dorbell
  • 1,569
  • 1
  • 13
  • 26
  • What is the other end of the `GroupAdmin` nav property? Shouldn't you have an `AdministeredGroup` property in the `User`class? – Asad Saeeduddin Jul 24 '14 at 13:39
  • The relation is optional from the users end, since not everyone is an admin for their group, but I could have added a nullable AdministeredGroup property. This does not solve my problem however. – Robin Dorbell Jul 24 '14 at 13:43
  • 1
    What is the problem exactly? What does the generated schema look like? – Cameron Jul 24 '14 at 13:44
  • @robindorbell Your relationship is "optional" from the users end, but you have no property to hold it (also, not sure what you mean by nullable `AdministeredGroup`; all non primitive types are by definition nullable). Either drop the `WithOptional` from your fluent config, or add a property to hold the optional mapping from user to administered group, and explicitly specify it in the `WithOptional` method call. – Asad Saeeduddin Jul 24 '14 at 13:46
  • @Asad As I understand it it's not needed for `WithOptional` to specify a property. I tried both the ways you're recommending but without success. This is the error message I recieve: Group_GroupAdmin_Source: : Multiplicity is not valid in Role 'Group_GroupAdmin_Source' in relationship 'Group_GroupAdmin'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'. – Robin Dorbell Jul 24 '14 at 13:53
  • @robindorbell Oh, the problem you're having is that one to many isn't supported on non key properties. In other words, unique constraints aren't recognised by EntityFramework (yet). Nevertheless, you can make this work by removing the `GroupAdminId` property, and instead adding a collection `AdministeredGroups` to `User`, with the foreign key being `GroupId`. You can still keep `GroupAdmin` if you want. – Asad Saeeduddin Jul 24 '14 at 13:57
  • Let me put it another way: if you had multiple groups with the same `GroupAdminId`, which group would Entity framework show in your optional `AdministeredGroup` property? The correct multiplicity of the relationship is one to many, since many (or no) groups can have the same admin. – Asad Saeeduddin Jul 24 '14 at 14:08
  • @Asad Wow, thanks! Worked like a charm. So this is not yet a feature? I'm not a idiot after all? – Robin Dorbell Jul 24 '14 at 14:14
  • Found this while searching btw: http://stackoverflow.com/questions/5346870/entity-framework-code-first-how-can-i-create-a-one-to-many-and-a-one-to-one-rel It suggests the same solution @Asad presented. – Robin Dorbell Jul 24 '14 at 14:14
  • @robindorbell Nope. This is actually one of the most frequently complained about limitations in EF. – Asad Saeeduddin Jul 24 '14 at 14:19

2 Answers2

0

Using Entity Framework alone, you cannot enforce the rule that your GroupAdmin needs to be in your Members List (which should by the way be of type ICollection if you want LazyLoading)

These kind of rules should not reside in the persistence layer, they belong in some kind of business layer where you can specify that a Group for which the GroupAdmin is not contained in the Memebers List is invalid.

rmtz
  • 646
  • 5
  • 5
0
  1. Groups is missing in User class.
  2. GroupAdminID is required in Group class, so you cannot make it optional.

Code

public class User
{
   public User()
   {
      Groups = new List<Group>();
   }
   public virtual ICollection<Group> Groups { get; set; }
   ....
}

public partial class Group
{
    public Group()
    {
        Members = new List<User>();
    }
    public virtual ICollection<User> Members { get; set; }
    ....
}

modelBuilder.Entity<User>()
    .HasRequired(u => u.Group)
    .WithMany(u => u.Members)
    .HasForeignKey(u => u.GroupID);

modelBuilder.Entity<Group>()
    .HasRequired(g => g.GroupAdmin)
    .WithMany(g => g.Groups)
    .HasForeignKey(d => d.GroupAdminID);
Win
  • 61,100
  • 13
  • 102
  • 181