Sh Hue Nga
Sh Hue Nga

Reputation: 23

Composite foreign key as primary key

I am currently migrating EF Core 3.0 code-first entity to clean architecture approach.

In EF Core 3.0 this works fine:

namespace SmartCom.Models
{
    public class branch
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [MaxLength(128)]
        public virtual string CompanyId { get; set; }

        [MaxLength(128)]
        public string AddressId { get; set; }

        public DateTime CreatedDate { get; set; }

        public int RefNo { get; set; }

        [ForeignKey("AddressId")]
        public address Address { get; set; }
    }
}

At the DB context

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<branch>()
  .HasKey(c => new { c.CompanyId, c.BranchId });

With clean architecture, I separated entity business logic from persistence as follows:

  1. Business logic model without persistence settings;
namespace SmartComCA.CoSec.Domain.Entities
{
    public class Branch
    {
        public virtual Company Company { get; set; }
        public Address Address { get; set; }
        public DateTime CreatedDate { get; set; }
        public int RefNo { get; set; }
    }
}
  1. Persistence configuration in Infrastructure project:
namespace SmartComCA.CoSec.Infrastructure.Persistence.Configuration
{
    public class BranchConfiguration : IEntityTypeConfiguration<Branch>
    {
        public void Configure(EntityTypeBuilder<Branch> builder)
        {
            //builder.HasKey(t => new { t.Company, t.Address});

            builder.HasOne(t => t.Company).WithMany()
                .HasForeignKey("CompanyId");

            builder.HasOne(t => t.Address).WithMany()
                .HasForeignKey("AddressId");

            builder.ToTable("branch");
        }
    }
}

This compiles but fails during add-migration. How do I specify composite foreign key as primary key in clean architecture where persistence is abstracted from business logic?

Upvotes: 2

Views: 1359

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205899

You can replace the explicit properties with shadow properties. Which you already did indirectly with HasForeignKey fluent API, but following is the explicit definition matching the original definition which also correctly configures the max length for string data types:

builder.Property<string>("CompanyId")
    .IsRequired()
    .HasMaxLength(128);

builder.Property<string>("BranchId")
    .IsRequired()
    .HasMaxLength(128);

Then you can define the composite PK using the shadow property names:

builder.HasKey("CompanyId", "BranchId");

But please note that having shadow PK imposes some limitations/requirements for operations like update and delete, since they would require having the loaded related objects rather than just their keys.

Upvotes: 2

Related Questions