简体   繁体   中英

EF Core base entity class inheritance and readonly properties

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.

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM