EF Core and derived classes

55 Views Asked by At

I have the following architecture.

Base classes:

public abstract class BaseDevice<TDeviceSync> : IPosition, IChainByPreviousId
    where TDeviceSync : BaseDeviceSync
{
    public virtual TDeviceSync AddedSync { get; set; }

    public virtual TDeviceSync? DeletedSync { get; set; }


    public int Id { get; set; }
    public string AddedSyncId { get; set; } = string.Empty;
    public string? DeletedSyncId { get; set; }
    public int? Position { get; set; }

    public int? PreviousDeviceId { get; set; }
    public virtual BaseDevice<TDeviceSync>? PreviousDevice { get; set; }

}

public abstract class BaseDeviceSync
{

    public string Id { get; set; } = Guid.NewGuid().ToString();
    public DateTime SyncDate { get; set; } = DateTime.Now;
    public string Filename { get; set; }
}

public abstract class BaseDeviceHistory<TBaseDevice, TDeviceSync> 
    where TBaseDevice : BaseDevice<TDeviceSync> 
    where TDeviceSync : BaseDeviceSync
{
    public int DeviceId { get; set; }
    public virtual TBaseDevice Device { get; set; }

    public string SyncId { get; set; }
    public string OldValue { get; set; }
    public string NewValue { get; set; }
}

and "one group" of derived classes:

public class RegisteredDevice : BaseDevice<RegisteredDeviceSync>
{
    public string DeviceName { get; set; }
    public string ModelNumber { get; set; }
    public string SoftwareVersion { get; set; }
    public string EldIdentifier { get; set; }
    public string CompanyName { get; set; }
    public string Email { get; set; }
    public string Website { get; set; }
    public string? DataTransferOptions { get; set; }

    public virtual RegisteredDeviceSync AddedSync { get; set; }
    public virtual RegisteredDeviceSync? DeletedSync { get; set; }

    private ICollection<RegisteredDeviceHistory> _history;
    public virtual ICollection<RegisteredDeviceHistory> History { get => _history ??= new List<RegisteredDeviceHistory>(); protected set => _history = value; }
}

public class RegisteredDeviceHistory : BaseDeviceHistory<RegisteredDevice, RegisteredDeviceSync>
{
    public virtual RegisteredDeviceSync RegisteredDeviceSync { get; set; }
    public RegisteredDeviceFieldName FieldName { get; set; }
}

public class RegisteredDeviceSync : BaseDeviceSync
{
    private ICollection<RegisteredDeviceHistory> _history;
    public virtual ICollection<RegisteredDeviceHistory> History { get => _history ??= new List<RegisteredDeviceHistory>(); protected set => _history = value; }

    private ICollection<RegisteredDevice> _addedDevices;
    public virtual ICollection<RegisteredDevice> AddedDevices { get => _addedDevices ??= new List<RegisteredDevice>(); protected set => _addedDevices = value; }

    private ICollection<RegisteredDevice> _deletedDevices;
    public virtual ICollection<RegisteredDevice> DeletedDevices { get => _deletedDevices ??= new List<RegisteredDevice>(); protected set => _deletedDevices = value; }
}

(I have also more "groups" of classes, derived these 3 abstract base classes and common services to update appropriate entities, but this part is not major now)

Also, I have the following implementations of IEntityTypeConfiguration interface:

public class RegisteredDeviceMap : IEntityTypeConfiguration<RegisteredDevice>
{
    public void Configure(EntityTypeBuilder<RegisteredDevice> builder)
    {
        builder.ToTable(nameof(RegisteredDevice));
        builder.HasKey(p => p.Id);

        builder.Property(p => p.EldIdentifier).HasMaxLength(6).IsFixedLength();
        builder.Property(p => p.Phone).HasMaxLength(128);
        builder.Property(p => p.Email).HasMaxLength(256);

        builder.Property(p => p.AddedSyncId).HasMaxLength(64);
        builder.Property(p => p.DeletedSyncId).HasMaxLength(64);


        builder.HasOne(a => a.AddedSync)
            .WithMany(a => a.AddedDevices)
            .HasForeignKey(p => p.AddedSyncId)
            .OnDelete(DeleteBehavior.Restrict);

        builder.HasOne(a => a.DeletedSync)
            .WithMany(a => a.DeletedDevices)
            .HasForeignKey(p => p.DeletedSyncId)
            .OnDelete(DeleteBehavior.Restrict);

        builder.HasOne(a => a.Vendor)
            .WithMany(a => a.RegisteredDevices)
            .HasForeignKey(a => a.VendorId)
            .OnDelete(DeleteBehavior.SetNull);

        builder.HasOne(a => a.PreviousDevice)
            .WithOne()
            .HasForeignKey<BaseDevice<RegisteredDeviceSync>>(p => p.PreviousDeviceId)
            .OnDelete(DeleteBehavior.NoAction);
    }
}

(and similar for RegisteredDeviceSync and RegisteredDeviceHistory)

DataContext:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  modelBuilder.Entity<BaseDevice<BaseDeviceSync>>().UseTpcMappingStrategy();
  modelBuilder.ApplyConfigurationsFromAssembly(typeof(RegisteredDeviceMap).Assembly);
}

but when I try to create migrations I receive:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: A key cannot be configured on 'RegisteredDevice' because it is a derived type. The key must be configured on the root type 'BaseDevice'. If you did not intend for 'BaseDevice' 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.

on line: builder.HasKey(p => p.Id);

Why so and how to fix?

Thanks

0

There are 0 best solutions below