Mapping One-to-Zero-or-One with EF7

asp.net-core c# entity-framework entity-framework-core sql-server

Question

I am currently in the process of cleaning up a fairly large database. Part of the database has a relationship which is a one-to-zero-or-one mapping. Specifically:

User -> UserSettings

Not all users will have user settings, but a user setting cannot exist without the user. Unfortunately, the tables already exist. User has an PK ID. UserSettings has a PK ID and a column, User_Id_Fk which, at this point in time, is not a true FK (there is no relationship defined).

I'm in the process of fixing that and have done so from the DB perspective through SQL and have confirmed with tests. (Added the FK constraint. Added a unique constraint on User_Id_Fk.) This was all done on the UserSettings table. (Note: I am not using EF Migrations here. I have to manually write the SQL at this point in time.)

However, I now need to wire up an existing application to properly handle this new mapping. The application is using ASP.NET Core 1.0 and EF7. Here are (shortened) versions of the existing data models.

public class User
{
  public int Id { get; set; }

  public virtual UserSettings UserSettings { get; set; }
}

public class UserSettings
{
  public int Id { get; set; }

  [Column("User_Id_Fk")]
  public int UserId { get; set; }

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

I have this Fluent Mapping as well:

builder.Entity<UserSettings>()
            .HasOne(us => us.User)
            .WithOne(u => u.User)
            .IsRequired(false);

When I go to run the application and access these items in the database, I get this error followed with a cryptic set of messages that has no information relating directly back to my application.:

ArgumentNullException: Value cannot be null.
Parameter name: navigation
Microsoft.Data.Entity.Utilities.Check.NotNull[T] (Microsoft.Data.Entity.Utilities.T value, System.String parameterName) <0x10d28a650 + 0x00081> in <filename unknown>, line 0

After doing research, someone had mentioned that the ID of the UserSettings class must be the same as the foreign key, like so:

public class UserSettings
{
  [Key, ForeignKey("User")]
  public int Id { get; set; }

  public virtual User User { get; set; }
}

I don't really have this as an option as the DB is being used for other applications I have no control over at this point. So, am I stuck here? Will I just have to maintain a 1:many mapping (which could happen now, though it hasn't) and not have proper constraints for a 1:0..1 mapping?

Update Looking at octavioccl's answer below, I tried it out without any success. However, I then removed User from the mapping in UserSettings (but I left UserId). Everything appeared to work as far as I can tell. I'm really confused what is going on here, however, and if this is even the right answer, or if I'm just getting lucky.

1
4
4/10/2016 7:39:07 PM

Popular Answer

Remove the data annotations and try with these configurations:

builder.Entity<UserSettings>()
            .Property(b => b.UserId)
            .HasColumnName("User_Id_Fk");

builder.Entity<User>()
            .HasOne(us => us.UserSettings)
            .WithOne(u => u.User)
            .HasForeignKey<UserSettings>(b => b.UserId);

From EF Core documentation:

When configuring the foreign key you need to specify the dependent entity type - notice the generic parameter provided to HasForeignKey in the listing above. In a one-to-many relationship it is clear that the entity with the reference navigation is the dependent and the one with the collection is the principal. But this is not so in a one-to-one relationship - hence the need to explicitly define it.

The example that is presented in the quoted link (Blog-BlogImage) is pretty much the same of what are you trying to achieve.

If the solution that I show above doesn't work, then you should check if User_Id_Fk column allows null. If that is the case, change the FK property type to int?:

public class UserSettings
{
  public int Id { get; set; }

  public int? UserId { get; set; }

  public virtual User User { get; set; }
}
4
8/9/2017 9:30:32 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow