First a piece of advice, try to study the differences between TPH, TPC and TPT, so you can choose the best option for each scenario.
TPH - Table by Hieraquia - Will have a single table for all Entities.
TPC - Table by Concrete - You will have a table for each concrete type.
TPT - Table by Type - You will have a table for each type (abstract or concrete).
In your specific case, I advise you to make Servico an abstract class, to prevent it from being instantiated.
For example, think of the entities Pessoa, PessoaFisica and PessoaJuridica, while it is interesting to have the ability to consult all people. It is important that the user always register a PessoaFisica or PessoaJuridica.
now I will post the implementation for each strategy.:
TPT - Table by Type
public class MyContext : DbContext
{
public MyContext()
{
}
public DbSet<Servico> Servicos { get; set; }
public DbSet<ServicoA> ServicosA { get; set; }
public DbSet<ServicoB> ServicosB { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
[Table("Servicos")]
public abstract class Servico
{
[Key]
public Guid ServicoID { get; set; }
public string Descricao { get; set; }
}
[Table("ServicosA")]
public class ServicoA : Servico
{
public decimal ValorA { get; set; }
}
[Table("ServicosB")]
public class ServicoB : Servico
{
public decimal ValorB { get; set; }
}
TPH - Table by Hieraquia
public class MyContext : DbContext
{
public MyContext()
{
}
public DbSet<Servico> Servicos { get; set; }
public DbSet<ServicoA> ServicosA { get; set; }
public DbSet<ServicoB> ServicosB { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
[Table("Servicos")]
public abstract class Servico
{
[Key]
public Guid ServicoID { get; set; }
public string Descricao { get; set; }
}
public class ServicoA : Servico
{
public decimal ValorA { get; set; }
}
public class ServicoB : Servico
{
public decimal ValorB { get; set; }
}
Note the removal of the attribute [Table] of entities ServicoA and ServicoB.
TPC - Table by Concrete
public class MyContext : DbContext
{
public MyContext()
{
}
public DbSet<ServicoA> ServicosA { get; set; }
public DbSet<ServicoB> ServicosB { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public abstract class Servico
{
[Key]
public Guid ServicoID { get; set; }
public string Descricao { get; set; }
}
[Table("ServicosA")]
public class ServicoA : Servico
{
public decimal ValorA { get; set; }
}
[Table("ServicosB")]
public class ServicoB : Servico
{
public decimal ValorB { get; set; }
}
Note that the entity Servico is no longer mapped.
Finally, I will put the script generated by Migrations for each of the above situations.:
TPT - Table by Type
CREATE TABLE [dbo].[Servicos] (
[ServicoID] [uniqueidentifier] NOT NULL,
[Descricao] [nvarchar](max),
CONSTRAINT [PK_dbo.Servicos] PRIMARY KEY ([ServicoID])
)
CREATE TABLE [dbo].[ServicosA] (
[ServicoID] [uniqueidentifier] NOT NULL,
[ValorA] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_dbo.ServicosA] PRIMARY KEY ([ServicoID])
)
CREATE INDEX [IX_ServicoID] ON [dbo].[ServicosA]([ServicoID])
CREATE TABLE [dbo].[ServicosB] (
[ServicoID] [uniqueidentifier] NOT NULL,
[ValorB] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_dbo.ServicosB] PRIMARY KEY ([ServicoID])
)
CREATE INDEX [IX_ServicoID] ON [dbo].[ServicosB]([ServicoID])
ALTER TABLE [dbo].[ServicosA] ADD CONSTRAINT [FK_dbo.ServicosA_dbo.Servicos_ServicoID] FOREIGN KEY ([ServicoID]) REFERENCES [dbo].[Servicos] ([ServicoID])
ALTER TABLE [dbo].[ServicosB] ADD CONSTRAINT [FK_dbo.ServicosB_dbo.Servicos_ServicoID] FOREIGN KEY ([ServicoID]) REFERENCES [dbo].[Servicos] ([ServicoID])
TPH - Table by Hieraquia
CREATE TABLE [dbo].[Servicos] (
[ServicoID] [uniqueidentifier] NOT NULL,
[Descricao] [nvarchar](max),
[ValorA] [decimal](18, 2),
[ValorB] [decimal](18, 2),
[Discriminator] [nvarchar](128) NOT NULL,
CONSTRAINT [PK_dbo.Servicos] PRIMARY KEY ([ServicoID])
)
TPC - Table by Concrete
CREATE TABLE [dbo].[ServicosA] (
[ServicoID] [uniqueidentifier] NOT NULL,
[ValorA] [decimal](18, 2) NOT NULL,
[Descricao] [nvarchar](max),
CONSTRAINT [PK_dbo.ServicosA] PRIMARY KEY ([ServicoID])
)
CREATE TABLE [dbo].[ServicosB] (
[ServicoID] [uniqueidentifier] NOT NULL,
[ValorB] [decimal](18, 2) NOT NULL,
[Descricao] [nvarchar](max),
CONSTRAINT [PK_dbo.ServicosB] PRIMARY KEY ([ServicoID])
)
Finally, note that the strategy TPC is more limited than the other two. For example, if you want to seek out all the services, you have to make a union between the two collections.
Already the choice between TPH and to TPT, is a trade-off between number of columns to be recovered from the bank (TPH brings everything) and the cost to realize JOINS(in the case of TPT, will need Joins between tables, and this has a cost, possibly an index will become necessary).
Although there isn’t a silver bullet for which strategy to choose, if the bank is to be manipulated only by its solution (without direct human intervention in the database), I would say to stay with the TPH, because this will have a superior performance when compared to the TPT.
And as for your doubt with delete, when deleting an entity of the Type ServicoB, records in tables will be deleted Servicos and ServicosB, and finally, a record in the table Servicos, will always have a child record in the table ServicosA or in the table ServicosB, but never both.
Example: https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt
– novic
Example: https://msdn.microsoft.com/en-us/library/jj618293(v=vs.113). aspx
– novic
Example: https://blogs.msdn.microsoft.com/alexj/2009/04/14/tip-12-how-to-choose-an-inheritance-strategy/
– novic