简体   繁体   中英

One-To-One Mapping issue

My SQL understanding is fairly basic and I come from a world of NHibernate so I'm fairly puzzled by this issue...

I have two classes:

public class UserProfile
{
    public UserProfile() { }

    [Key]
    public int UserId { get; set; }
    public string UserName { get; set; }
    public SerialNumber Serial { get; set; }
    // Other properties
}

and

public class SerialNumber
{
    public SerialNumber() 
    {
        // My code that creates a unique Code
    }

    [Key]
    public string Code { get; set; }
    public UserProfile User { get; set; }

    // Other Properties
}

I have this

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Other modelBuilder stuff

    modelBuilder.Entity<UserProfile>()
        .HasOptional(s => s.Serial)
        .WithRequired(u => u.User);
}

After I run Add-Migration I get this:

public override void Up()
{
    DropForeignKey("dbo.UserProfile", "Serial_Code", "dbo.SerialNumbers");
    DropIndex("dbo.UserProfile", new[] { "Serial_Code" });
    RenameColumn(table: "dbo.SerialNumbers", name: "Serial_Code", newName: "User_UserId");
    AddForeignKey("dbo.SerialNumbers", "User_UserId", "dbo.UserProfile", "UserId");
    CreateIndex("dbo.SerialNumbers", "User_UserId");
}

public override void Down()
{
    // Inverse of above
}

After I run Update-Database I get an error: "Either the parameter @objname is ambiguous or the claimed @objtype (COLUMN) is wrong."

I can tell by the code generated by Add-Migration that something is wrong as my db table doesn't even have a column by the name of "Serial_Code"

I need Code to be my Key as EntityFramework doesn't have an option to enforce a column to be Unique except to make it a key.

How do I make Serial to be an optional property of UserProfile and User in SerialNumber to be automatically set when it's added to UserProfile ?

By default, in one-to-one relationships, Entity Framework requires the foreign key on the dependent to also be the primary key. This is likely due to the fact that Entity Framework only sets unique constraints on primary keys (or rather the unique constraint is there by virtue of it being a primary key). On the level of the database, the one-to-one is not mapped onto both sides of the relationship, ie there's not a foreign key created on both tables. This makes sense when you think about how referential integrity works: if one side of the relationship is removed, it would cause database disintegrity for the other side, resulting in a sort of endless loop.

So, the foreign key is added to the dependent, which in the case of a one-to-zero-to-one like you have here is the end with the required navigation property: your SerialNumber model. That means that to truly create a one-to-one and not a one-to-many, your key on SerialNumber would need to be User_UserId (the auto-generated foreign key column, since you didn't manually specify a foreign key property). To set it up this way, your models would look like:

public class UserProfile
{
    [Key]
    public int UserId { get; set; }

    public SerialNumber Serial { get; set; }

    ...
}

public class SerialNumber
{
    [Key]
    [ForeignKey("User")]
    public int UserId { get; set; }

    public UserProfile User { get; set; }

    ...
}

Or with Fluent API:

modelBuilder.Entity<SerialNumber>()
    .HasRequired(m => m.User)
    .WithOptional(m => m.Serial);

(You would need to leave off the [Key] data annotation, so that EF can make the foreign key, they primary key for the table.)

That leaves you without a unique constraint on your Code property, though. You can of course manually set this constraint on your table, or if you're not using automatic migrations, you can alter your migration to create the constraint:

CreateIndex("dbo.SerialNumbers", "Code", unique: true);

The problem here is that Entity Framework will not handle unique validation for you. So, you'll need to maintain integrity manually within your code (ie, try to lookup a SerialNumber with the Code you're about to save, and only save it if you don't get anything back from the query).

It's kind of a pain, I know, but for the moment, that's all you can do. Hopefully, EF will finally address uniqueness in a future release.

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