简体   繁体   中英

EF Mapping Properties of an Entity Type to Multiple Tables with TPH Inheritance

I would like to use Mapping Properties of an Entity Type to Multiple Tables in the Database (Entity Splitting) whilst as the same time using Mapping the Table-Per-Hierarchy (TPH) Inheritance , therefore my model mapping code is as follows:

   modelBuilder
    .Entity<Person>()
    .HasKey(n => n.PersonId)
    .Map(map =>
    {
        map.Properties(p => new { p.Name });
        map.ToTable("dbo.Person");
    })
    .Map<Customer>(map =>
    {
        map.Requires("PersonType").HasValue("C");
        map.Properties(p => new { p.CustomerNumber });
        map.ToTable("dbo.Customer");
    });

Based upon the following underlying database schema:

create table dbo.Person
(
    PersonId int not null identity(1,1) primary key,
    PersonType char(1) not null,
    Name varchar(50) not null
)

create table dbo.Customer
(
    PersonId int not null references dbo.Person (PersonId),
    CustomerNumber varchar(10) not null
)

However, when the EF tries to execute my query:

ctx.People.ToList();

The following exception message is thrown:

Invalid column name 'PersonType'.

Running a SQL profile it would appear that its trying to use a predicate on the field PersonType with value C on the dbo.Customer table, rather than on the dbo.Person table where my discriminator really is.

If I use one or the other feature, ie only the inheritance or only the additional table mapping then it works but then I forfeit some of my requirements.

Can what I'm doing be done with the EF Fluent API?

Thanks for your time.

This can be achieved by creating a view on all the table schemas involved in the mapping:

create view dbo.vw_PersonExtended
as

    select
        p.Name, p.PersonId, p.PersonType, c.CustomerNumber
    from
        dbo.Person p
        left join dbo.Customer c on c.PersonId=p.PersonId

And mapping this view to the base class type Person and removing the derived class table mapping as follows:

   modelBuilder
    .Entity<Person>()
    .HasKey(n => n.PersonId)
    .Map(map =>
    {
        map.Properties(p => new { p.Name });
        map.ToTable("dbo.vw_PersonExtended");
    })
    .Map<Customer>(map =>
    {
        map.Requires("PersonType").HasValue("C");
        map.Properties(p => new { p.CustomerNumber });
    });

This would fail on inserting new entities as the view has more than one base table, therefore you'd have to use an INSTEAD OF TRIGGER or map the insertion to a stored procedure with Fluent code as:

    modelBuilder
        .Entity<Customer>()
        .MapToStoredProcedures(map => map.Insert(i => i.HasName("usp_InsertCustomer")));

And insert stored procedure example as:

create procedure dbo.usp_InsertCustomer
    @Name varchar(50),
    @CustomerNumber varchar(50)
as
begin

        set nocount on
        declare @id int

        insert into dbo.Person (Name, PersonType)
        values (@Name, 'C')
        set @id = scope_identity()

        insert into dbo.Customer (PersonId, CustomerNumber)
        values (@id, @CustomerNumber)

        select @id as PersonId

end

Obviously the drawback with this approach is all of the plumbing work involved with getting this working.

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