Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core / DbContext > Map custom type as primary key

Using the fluent api, how do I map a custom type as the primary key within my OnModelCreating method of the DbContext class?

Using EF Core I'm trying to build a model for the follow entity.

public class Account
{
    public AccountId AccountId { get; }

    public string Name { get; set; }

    private Account()
    {
    }

    public Account(AccountId accountId, string name)
    {
        AccountId = accountId;
        Name = name;            
    }
}

Where the primary key is the AccountId; the type is a simple value object like this.

public class AccountId
{
    public string Id { get; }

    public AccountId(string accountId)
    {
        Id = accountId;
    }
}

Within OnModelCreating, I found I can't map the AccountId without having a backing field. So I introduced the backing field _accountId. I don't want the AccountId to have a setter.

public class Account
{
    private string _accountId;
    public AccountId AccountId { get { return new AccountId(_accountId); } }

    public string Name { get; set; }

    private Account()
    {
    }

    public Account(AccountId accountId, string name)
    {
        _accountId = accountId.Id;
        Name = name;            
    }
}

But I still can't figure out how you specify a property with a backing field which is also the primary key.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    var account = modelBuilder.Entity<Account>();

    account.ToTable("Accounts");
    account.HasKey(x => x.AccountId);
    account.Property(x => x.AccountId).HasField("_accountId");
}

The OnModelCreating throws an exception on the property map line (account.Property(x => x.AccountId).HasField("_accountId");). Stating that property and field have to be the same type.

like image 594
Chris Moutray Avatar asked Apr 02 '18 07:04

Chris Moutray


People also ask

How do I create a composite primary key in Entity Framework Core?

The only way to configure composite keys is to use the HasKey method. You specify the properties that form the composite key by passing them in as properties of an anonymous type to the HasKey method.

How do you configure a primary key for an entity in Entity Framework fluent API?

Configuring a primary key By convention, a property named Id or <type name>Id will be configured as the primary key of an entity. Owned entity types use different rules to define keys. You can configure a single property to be the primary key of an entity as follows: Data Annotations.

What is the EF core convention for primary keys?

EF Core will create the primary key column for the property named Id or <Entity Class Name>Id (case insensitive). For example, EF Core will create a column as PrimaryKey in the Students table if the Student class includes a property named id, ID, iD, Id, studentid, StudentId, STUDENTID, or sTUdentID.


1 Answers

As pointed out, one can use a custom typed property as entity key by taking advantage of the Value Conversion feature in EF Core 2.1

So in your own example instead of mapping the property to a backing field, you can now define a custom conversion for it like this:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    ...
    account.HasKey(x => x.AccountId);
    account.Property(x => x.AccountId)
        .HasConversion(
            v => v.Id,
            v => new AccountId(v));
}

As described in the documentation, also a ValueConverter class can be implemented to make the conversion reusable and many custom converters are also provided out of the box.

Note: It's a good idea to implement IComparable and IComparable<T> for your custom AccountId class. Because EF Core seems to sort your changed entities based on their Keys internally for batch operations and you would receive an exception if your Key is not comparable!

like image 169
amiir Avatar answered Sep 17 '22 17:09

amiir