0
public interface IEntity<TId> 
{
    TId Id { get; set; }
}
    
public abstract class Auditable
{
    public int CreatedBy { get; set; }
    public User CreatedUser { get; set; } = null!;
    
    public DateTime Created { get; set; }
    
    public int LastModifiedBy { get; set; }
    public User ModifiedUser { get; set; } = null!;
    
    public DateTime? LastModified { get; set; }
}

public class AccountStatus : Auditable, IEntity<int>
{
    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Code { get; set; }
    public string? Description { get; set; }
}

public class AccountType : Auditable, IEntity<long>
{ 
   public long Id { get; set; }  ...
}

I am adding auditable base entity for existing Implemetation..when i inherited from Audtable entity I am getting error

A key cannot be configured on 'AccountStatus' because it is a derived type. The key must be configured on the root type 'Auditable'. If you did not intend for 'Auditable' to be included in the model, ensure that it is not referenced by a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation on a type that is included in the model.

Key may be int/long for other entity.

I cant move key(Id) to Base class because key may be int/long for other entity. How can I achieve this in EF Core 5? EDIT:

 public class AuditableMap : IEntityTypeConfiguration<Auditable>
    {
        public void Configure(EntityTypeBuilder<Auditable> builder)
        {
         
            builder.Property(t => t.Created).HasColumnType("timestamp without time zone");
            builder.Property(t => t.LastModified).HasColumnType("timestamp without time zone");
            builder.HasOne(t => t.CreatedUser).WithMany().HasForeignKey(t => t.CreatedBy).OnDelete(DeleteBehavior.Restrict);
            builder.HasOne(t => t.ModifiedUser).WithMany().HasForeignKey(t => t.LastModifiedBy).OnDelete(DeleteBehavior.Restrict);


        }
    }


 public class AccountStatusMap : IEntityTypeConfiguration<AccountStatus>
    {
        public void Configure(EntityTypeBuilder<AccountStatus> builder)
        {
            builder.ToTable("account_status", schema: "public");
            builder.HasKey(t =>t.Id);
            builder.Property(t =>t.Name).HasMaxLength(100);
            builder.Property(t => t.Code).HasMaxLength(10);
            builder.Property(t =>t.Description).HasMaxLength(200);
         

        }
    }
Ajt
  • 1,719
  • 1
  • 20
  • 42
  • 2
    not a direct answer, but you might use composition instead of inheritance: change Audittable to be an interface and derive from it. then this problem should be solved. if Auditable is a DTO without any logic, this should not be a problem. – Jan Jan 15 '21 at 05:06
  • can u provide sample code for the same/.. – Ajt Jan 15 '21 at 05:27
  • 3
    Note the error message: "If you did not intend for 'Auditable' to be included in the model, ensure that it is not referenced by a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation on a type that is included in the model.". Something in your model/configuration is letting EF Core consider `AuditableEntity` as *entity*, rather than a *base class* as it seems to be the intention. You have to find and correct that yourself, or post your model fluent configuration in order to tell you where is the problem and how to correct it. – Ivan Stoev Jan 15 '21 at 07:54
  • @IvanStoev i have updated the qtn..thanks – Ajt Jan 15 '21 at 08:25
  • 2
    It's because you have `IEntityTypeConfiguration`. That registers the class as an entity. You must use a generic configuration class, a bit like [this](https://stackoverflow.com/a/65275803/861716). You may also like [this approach with shadow properties](https://stackoverflow.com/a/52021425/861716). – Gert Arnold Jan 15 '21 at 08:43
  • @GertArnold thanks ..i will chck – Ajt Jan 15 '21 at 08:54

1 Answers1

2

If Auditable has no logic but is only a simple data-class, you could change it to be an interface:

public interface IAuditable
{
    int CreatedBy { get; set; }
    User CreatedUser { get; set; }; //I removed the null! here if need be, you might consider using Nullable-Types
    
    DateTime Created { get; set; }
    
    int LastModifiedBy { get; set; }
    User ModifiedUser { get; set; };
    
    DateTime? LastModified { get; set; }
}

public class AccountStatus : IAuditable, IEntity<int>
{
    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Code { get; set; }
    public string? Description { get; set; }

    public int CreatedBy { get; set; }
    public User CreatedUser { get; set; } = null!;
    
    public DateTime Created { get; set; }
    
    public int LastModifiedBy { get; set; }
    public User ModifiedUser { get; set; } = null!;
    
    public DateTime? LastModified { get; set; }
}

public class AccountType : IAuditable, IEntity<long>
{ 
public long Id { get; set; }  ...
//add all fields from the interface IAuditable here as well
}

This adds more ceremony as you have to type those fields in each derived class, but many IDE's support automatic generation of code for implemented interfaces.

If you have many classes that need to implement this interface but with varying ID-data-types you could create base classes like so:

public class AuditableIdInt: IAuditable 
{
    public int Id { get; set; }
    // all fields from the Interface
}

public class AuditableIdLong: IAuditable 
{
    public long Id { get; set; }
    // all fields from the Interface
}

of you might even consider a generic base class where the generic defines the ID-type. but I am not sure if this works with EF.

PS: I doubt that AccountType needs a long Id. Check the integer type list

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
Jan
  • 3,825
  • 3
  • 31
  • 51