Search code examples
c#asp.net-coreentity-framework-coreef-core-8.0

How to set ComplexTypes as primary key in EF Core


Is it possible to use complex types as primary keys in EF Core 8?

I have a complex type whose properties must be unique and treated as a primary key.

[ComplexType]
public class ContractID
{
    public readonly short BranchCode;
    public readonly short TypeCode;
    public readonly int CIF;
    public readonly int Counter;

    public ContractID(short branchCode, short typeCode, int cIF, int counter)
    {
        BranchCode = branchCode;
        TypeCode = typeCode;
        CIF = cIF;
        Counter = counter;
    }
}

And I have an entity that has the above complex type as its primary key:

public class Contract 
{
    public ContractID ID { get; set; }
    public long ApprovedAmount { get; set; }
    public long Amount { get; set; }
    public short Duration { get; set; }
    public byte Rate { get; set; } 
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

public class ContractConfiguration : IEntityTypeConfiguration<Contract>
{
    public void Configure(EntityTypeBuilder<Contract> builder)
    {
        builder.ToTable("Contracts");        
        builder.HasKey(x => x.ID);
        builder.ComplexProperty(x => x.ID);
        builder.Property(x => x.ID).ValueGeneratedNever();  
        builder.Property(x => x.StartDate).HasColumnType("datetime2");
        builder.Property(x => x.EndDate).HasColumnType("datetime2");
    }
}

But when running migration command

Add-Migration Init -Context AssistantDBContext

I get the following error :

Unable to create a 'DbContext' of type 'AssistantDBContext'. The exception 'The property or navigation 'ID' cannot be added to the 'Contract' type because a property or navigation with the same name already exists on the 'Contract' type.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

The link at the end of the message did not help me either. What is the meaning of error? Is there a solution and is it basically possible to do such a thing?

I expected that the problem would be solved by defining the complex type in OnModelCreating.


Solution

  • An easy way is to use inherit Contract: ContractID instead of complexType.
    If this doesn't meet your demand, you could try edit the migration file to achieve.
    Add empty contructor to ContractID

            [ComplexType]
            public class ContractID
            {
                public ContractID(){}
    
                public ContractID(short branchCode, short typeCode, int cIF, int counter)
                {
                   ...
                }
            }
    

    Modify the complexproperty columName. Set "amount" as key.

        public class ContractConfiguration : IEntityTypeConfiguration<Contract>
        {
            public void Configure(EntityTypeBuilder<Contract> builder)
            {
                builder.ToTable("Contracts");
                builder.Property(x => x.StartDate).HasColumnType("datetime2");
                builder.Property(x => x.EndDate).HasColumnType("datetime2");
    
                builder.OwnsOne(x => x.ID, p =>
                {
                    p.Property(y => y.BranchCode).HasColumnName("BranchCode");
                    p.Property(y => y.TypeCode).HasColumnName("TypeCode");
                    p.Property(y => y.CIF).HasColumnName("CIF");
                    p.Property(y => y.Counter).HasColumnName("Counter");
                });
    
                //set "Amount" as primary key temporary to modify later
                builder.Property(x => x.Amount).ValueGeneratedNever();
                builder.HasKey(x => x.Amount);
            }
        }
    
    

    After add-migration , open the migration file and Check Up method. Find following code enter image description here
    Replace the key with the composite key.

    constraints: table =>
    {
        table.PrimaryKey("PK_Contracts", x => new {x.BranchCode,x.TypeCode,x.CIF,x.Counter});
    })
    
    

    Then update the database with the modified composite key.