Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework (Code First) One to Many and One to One relations (with two entities). How to?

I'm trying to do this with EF Code First:

database model

There area two tables: Users and Areas. One User belongs to one required area and one Area can have zero or one User (as administrator). Then:

Users *..1 Areas and Users 0..1 Areas

The Users class:

public class User {
    public int UserId { get; set; }
    public string Name { get; set; }

    [ForeignKey("Area")]
    public int AreaId { get; set; }
    [InverseProperty("Users")]
    public virtual Area Area { get; set; }

    public virtual Area AreaTitular { get; set; }
}

The Areas class:

public class Area {
    public int AreaId { get; set; }
    public string Name { get; set; }

    public List<User> Users { get; set; }

    [ForeignKey("User")]
    public int? UserId { get; set; }
    [InverseProperty("AreaTitular")]
    public virtual User User{ get; set; }
}

And the error on update-database command:

Unable to determine the principal end of an association between the types 'TestEntityFrameworkCodeFirst.Model.Area' and 'TestEntityFrameworkCodeFirst.Model.User'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

Any help will be very appreciated :)

I'm not completely sure if this is ok:

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    modelBuilder.Entity<Area>()
        .HasOptional(i => i.User)
        .WithOptionalDependent();
    base.OnModelCreating(modelBuilder);
}

Just added this on the OnModelCreating on the Context class. And this is how it looks in SQL Server:

Database

like image 225
Mr. DMX Avatar asked Oct 19 '22 17:10

Mr. DMX


1 Answers

The problem is in the configuration of your one-to-one relationship because one end must be principal and second end must be dependent. When you are configuring this kind of relationship, Entity Framework requires that the primary key of the dependent also be the foreign key.So, don't map UsuarioId as FK, otherwise, EF requires that FK must be PK too (comment that property). The other problem is EF doesn't know who is the principal in your relationship. To especify who is the principal, use the Required attribute. If you, for example, add this attribute over AreaTitular, you are specifying that to create a User, the AreaTitular property must be set it after save changes, so your principal in this case is Area and the dependend is User, but this is hypothetical escenario, I don't know your logic. Check this posts for more info:

Now, it's possible especify both ends as optional, but one of them must be principal. If this is your case, I suggest you comment the Data Annotations attributes and configure the relationships using Fluent Api this way:

   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   {
        modelBuilder.Entity<User>()
            .HasRequired(a => a.Area)
            .WithMany(c => c.Usuarios)
            .HasForeignKey(a => a.AreaId);

        modelBuilder.Entity<Area>()
            .HasOptional(a => a.Usuario)
            .WithOptionalPrincipal(u => u.AreaTitular);
   }

I suggest you read this page to understand better the one-to-one relationships.

like image 124
octavioccl Avatar answered Oct 22 '22 22:10

octavioccl