How to use a custom table prefix in combination with TPH in Entity Framwork Core?

53 Views Asked by At

Disclaimer: I'm relatively unexperienced in C# / WPF / EFC

I'm working on a C# WPF application which needs to store some data in a database. To store this data i want to use Entity Framework Core. The application will uses a shared database instance which is also used by other applications. Each application has its own table prefix so my application needs to have an own table prefix as well.

Update: What i want to achieve is that all objects which are inherited from Parent are stored in the same table. When i "load" data from the table i want an instantiated instance of conserning Child class. It is/should not possible to have instances of Parrent them self.

Add table prefix:

I added the table prefixes in the OnModelCreating function of my DbContext class. (This works great so far)

foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
    modelBuilder.Entity(entity.ClrType).ToTable(tablePrefix + entity.GetTableName());
}

Implement "Table per Hierarchy":

After implementing the table prefix i implemented the TPH according to this description https://www.learnentityframeworkcore.com/inheritance/table-per-hierarchy This is the implementation:

public abstract class Parent
{
    ...
}

public class ChildA : Parent
{
    ...
}

public class ChildB : Parent
{
    ...
}

internal class EcroDbContext : DbContext
{
    private string tablePrefix = "APP_PREFIX_";

    public DbSet<Schedule> Schedules { get; set; } = null;

    public DbSet<ChildA> ChildA { get; set; } = null;
    public DbSet<ChildBIpp> ChildB { get; set; } = null;

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string user = "xxx";
        string password = "xxx";
        string dbName = "xxx";
        string conString = "User Id=" + user + ";Password=" + password + ";Data Source=" + dbName;

        optionsBuilder.UseOracle(conString, x => x.MigrationsHistoryTable(tablePrefix + "ef_migration_history", null));
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Parent>().ToTable(tablePrefix + "Jobs");

        // Add prefix to each table.
        // When i remove the foreach bellow, the TPH works fine
        foreach (var entity in modelBuilder.Model.GetEntityTypes())
        {
            modelBuilder.Entity(entity.ClrType).ToTable(tablePrefix + entity.GetTableName());
        }
    }
}

When i run Add-Migration IntialMigration i got this error: Unable to create a 'DbContext' of type ''. The exception 'Both 'ChildB' and 'ChildA' are mapped to the table 'APP_PREFIX_APP_PREFIX_APP_PREFIX_Jobs'. All the entity types in a non-TPH hierarchy (one that doesn't have a discriminator) must be mapped to different tables.

The error says that all entity types in a non-TPH must map a different table but i thought i had implemented TPH for the mentioned classes. First i thought that i implementated it wrong but when i remove the table prefix part from the OnModelCreating part, it seems to work fine. This means that the table prefix implementation causes issues for the TPH implementation. How can i solve this error?

[SOLVED] I notice that the prefix is added 3 times for the Jobs table. How can i solve this?

1

There are 1 best solutions below

0
CodeNinja On

I solved the naming issue by checking if the name already contains the prefix (like @ Svyatoslav Danyliv suggested).

            foreach (var entity in (modelBuilder.Model.GetEntityTypes()))
            {
                if (!entity.GetTableName().Contains(tablePrefix))
                {
                    modelBuilder.Entity(entity.ClrType).ToTable(tablePrefix + entity.GetTableName());
                }
            }

This also solved the TPH issue for some reason. It works like a charm now.