Reputation: 2115
Is it possible to do something like this:
public abstract class BaseEntity : IEntity
{
private Guid _id;
private DateTime _createdOn;
private DateTime _modifiedOn;
private byte[] _timestamp;
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id => _id;
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedOn => _createdOn;
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime ModifiedOn => _modifiedOn;
[Timestamp]
public byte[] Timestamp => _timestamp;
}
This is base class, all entities should inherit from this class. Also this properties should be readonly (I'm using backing properties), because EF need to take care of them.
public class Category : BaseEntity
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
And OnModelCreating
is empty.
When I run migrations, I get message :
'The entity type 'Project.Database.Models.Category' requires a primary key to be defined.
Upvotes: 1
Views: 4331
Reputation: 205529
This seems to be a bug, because it works if you provide public
, internal
or protected
(but not private
or none) setter for the Id
property, or if you specify the key of the derived entity explicitly via Fluent API
modelBuilder.Entity<Category>().HasKey(e => e.Id);
which of course defeats the whole purpose of using the base class property.
You might consider reporting it to EF Core GitHub.
Upvotes: 1
Reputation: 236
The problem is that your BaseEntity.Id property is read only.
Your Id property is implemented as an expression-bodied member (the => syntax), which is merely syntactic sugar for a get-only property.
So, your code:
public Guid Id => _id;
is equivalent to:
public Guid Id { get { return _id; } }
EF must be able to set the values of mapped properties. You'll have to use traditional syntax to implement the property, and you'll have to provide a setter. If your context is in the same assembly as your model, you could mark the setter as internal so that the property is read only outside the assembly. If your context is in a different assembly, you could still mark the setter as internal, then use the InternalsVisibleTo attribute to make internals visible to the assembly containing your context.
Upvotes: 2