简体   繁体   中英

Entity Framework: One to zero relationship

I've got

public class MyUser : IdentityUser<int,MyLogin,MyUserRole,MyUserClaim>
{
     public virtual MyUserProfile Profile { get; set; }
}

public class MyUserProfile
{
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
}

My implementation of IdentityUser has INT PK. I want to have one navigation property in MyUser and don't have on the other side. I prefer data annotations. Is it possible to use IdentityUser PK for navigating? (EF v 6.1.3)

UPD #1. Dependent type - MyUserProfile. And it DOES NOT have to contain navigation property of MyUser type.

I'd question why it is you're doing this, a better solution may be to keep it on a single table as it will be faster unless they are both massive entities and the profile is optional (but even then, you could have a separate column stating that the extra data is or isn't available).

Since you seem to be asking for a 1:0..1 relationship you can implement it as follows. I know you wanted Data Annotations, but I'd really recommend being explicit here*, so that any changes to your model won't cause a potentially unintended knock-on effect.

public class MyUser : IdentityUser<int,MyLogin,MyUserRole,MyUserClaim>
{
     public virtual MyUserProfile Profile { get; set; }
}

public class MyUserProfile
{
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }

     public MyUser User { get; set; }
}

On your DbContext , override OnModelCreating with the following:

modelBuilder.Entity<MyUserProfile>()
            .HasRequired(m => m.User)
            .WithOptional(m => m.Profile);

** Full disclosure: I also always prefer Data Annotations over the Fluent Configuration Syntax, but I haven't avoided it to date and there's really no downside to it. For something as fundamental as this, you don't want it to be affected by a slight change in attribute.

This generates the following schema that keeps things lean as the IDs are synchronised between the two tables as we'd expect:

        CreateTable(
            "dbo.MyUsers",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Test = c.String(),
                })
            .PrimaryKey(t => t.Id);

        CreateTable(
            "dbo.MyUserProfiles",
            c => new
                {
                    Id = c.Int(nullable: false),
                    Test = c.String(),
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("dbo.MyUsers", t => t.Id)
            .Index(t => t.Id);

As @grek40 mentioned, it can be done without the navigation property on the other end by flipping the fluent configuration of the relationship (see the comment for more info), see grek40's answer for that option.

I want to mention a slight difference of my tests compared to the answer of @RudiVisser regarding the created database.

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

    public virtual MyUserProfile Profile { get; set; }
}

public class MyUserProfile
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}


class MyContext : DbContext
{
    public DbSet<MyUser> Users { get; set; }
    public DbSet<MyUserProfile> Profiles { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MyUser>()
            .HasOptional(x => x.Profile)
            .WithRequired();
    }
}

Generated Migration

public override void Up()
{
    CreateTable(
        "dbo.MyUserProfiles",
        c => new
            {
                Id = c.Int(nullable: false),
                FirstName = c.String(),
                LastName = c.String(),
            })
        .PrimaryKey(t => t.Id)
        .ForeignKey("dbo.MyUsers", t => t.Id)
        .Index(t => t.Id);

    CreateTable(
        "dbo.MyUsers",
        c => new
            {
                Id = c.Int(nullable: false, identity: true),
            })
        .PrimaryKey(t => t.Id);
}

In contrast to his answer, I don't observe the creation of a separate MyUser_Id foreign key. The MyUserProfiles.Id is correctly configured as primary key and foreign key. So anyone who wants to use such a configuration should give it a try but carefully observe the generated migration code.

My toolset: Visual Studio 2013, EntityFramework 6.1.3 nuget package.

I'm note sure if you want that, but you can "split table". In other words use the same table for the two entities.

Like this:

[Table("MyUser ")]
public class MyUser : IdentityUser<int,MyLogin,MyUserRole,MyUserClaim>
{
     public virtual MyUserProfile Profile { get; set; }
}

[Table("MyUser ")]
public class MyUserProfile
{
     [Key]
     public int Id { get; set; }

     public string FirstName { get; set; }
     public string LastName { get; set; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM