Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core: Using ID as Primary key and foreign key at same time

Tags:

I have two entities, Prospect and Person, what I'm trying to do is use Prospect.ID as the primary key on Prospect table and as the foreign key of PersonID, my idea is use the same ID for both entities without the need of a PersonID on my Prospect entity. When the prospect is being saved on database, it tries to save a PersonID even though I don't have this property on my Prospect entity, I would like to know if EF core supports this kind of relationship.

Here's what I got on my model builder:

modelBuilder.Entity<ProspectDto>(builder => { builder.ToTable("Prospects"); builder.HasKey(prospect => prospect.ID); });

modelBuilder.Entity<PersonDto>(builder => { builder.HasOne(p => p.Prospect).WithOne().HasForeignKey<ProspectDto>(pe => pe.ID); });

Here's what is being executed on database:

INSERT INTO [Prospects] ([ID], [PersonID]) VALUES (@p421, @p422)

PersonDto:

public class PersonDto : DtoBase
{
    public PersonDto()
    {
        
    }
  
    public ProspectDto Prospect { get; set; }
}

ProspectDto:

public class ProspectDto : DtoBase
{
    public ProspectDto()
    {

    }

    public PersonDto Person { get; set; } = new PersonDto();
}

DtoBase:

public abstract class DtoBase
{
    public Guid ID { get; protected set; }
}

Thanks.

like image 874
Bruno Joaquim Avatar asked Dec 21 '16 11:12

Bruno Joaquim


People also ask

Is it possible to have a primary key and a foreign key both in one table?

A table can have only one Primary Key. A table can have any number of Foreign Keys. The primary key is unique and Not Null.

Should I use EF6 or EF core?

Keep using EF6 if the data access code is stable and not likely to evolve or need new features. Port to EF Core if the data access code is evolving or if the app needs new features only available in EF Core. Porting to EF Core is also often done for performance.

How do you create a composite primary key in EF core?

Entity Framework Core supports composite keys - primary key values generated from two or more fields in the database. Composite keys are not covered by conventions or data annotation attributes. The only way to configure composite keys is to use the HasKey method.

Is EF core faster than EF6?

EF Core 6.0 itself is 31% faster executing queries. Heap allocations have been reduced by 43%.


2 Answers

Using attributes only, without FluentAPI:

public abstract class DtoBase
{
    [Key]
    public Guid ID { get; protected set; }
}

public class PersonDto : DtoBase
{
    [InverseProperty("Person")]
    public ProspectDto Prospect { get; set; }
}

public class ProspectDto : DtoBase
{
    [ForeignKey("ID")]           // "magic" is here
    public PersonDto Person { get; set; } = new PersonDto();
}

I don't know what is equivalent of ForeignKey in FluentAPI. All other (Key and InverseProperty) are configurable, but why use two methods instead one.

Code above generates following migration code:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateTable(
        name: "Persons",
        columns: table => new
        {
            ID = table.Column<Guid>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Persons", x => x.ID);
        });

    migrationBuilder.CreateTable(
        name: "Prospects",
        columns: table => new
        {
            ID = table.Column<Guid>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Prospects", x => x.ID);
            table.ForeignKey(
                name: "FK_Prospects_Persons_ID",
                column: x => x.ID,
                principalTable: "Persons",
                principalColumn: "ID",
                onDelete: ReferentialAction.Cascade);
        });
}

Looks very close to what you need.

like image 191
Dmitry Avatar answered Sep 24 '22 09:09

Dmitry


Here is the FluentAPI equivalent of @dmitry's solution:

// Model classes:
public abstract class DtoBase
{
    public Guid ID { get; protected set; }
}

public class PersonDto : DtoBase
{
    public ProspectDto Prospect { get; set; }
}

public class ProspectDto : DtoBase
{
    public PersonDto Person { get; set; } = new PersonDto();
}

-------------------------------------------------------------------

// DbContext's OnModelCreating override:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<ProspectDto>().HasOne(p => p.Person).WithOne().HasForeignKey<ProspectDto>(p => p.ID);
}
like image 23
Dominik Avatar answered Sep 23 '22 09:09

Dominik