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.
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.
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.
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With