viernes, 30 de agosto de 2013

[Entity Framework][Code First] Herencia - Tabla por tipo concreto - Table per Concrete Type (TPC)

 

Introducción


Se continua analizando las diferentes implementaciones de herencia que Entity Framework nos permite modelar.

En este caso cada tipo mapeara a una tabla la cual define todos los campos, ya sean los particulares que este defina, así como los declarados en la clase base.

Este artículo utilizara el mismo modelo de los ejemplos anteriores:

[Entity Framework][Code First] Herencia - Tabla por tipo - Table per Type (TPT)

[Entity Framework][Code First] Herencia - Tabla por jerarquía - Table per Hierarchy (TPH)

 

Definición del modelo


Partiremos de un modelo de clases ya conocido en los artículos anteriores.

image

y obtendremos las tablas

image

La definición de cada clase derivada creara una tabla para si misma, llevando no solo las propiedades que esta defina como columnas sino que también lo hará con las de la clase base.

Un punto que se debe remarcar es que no hay relación entre las tablas.

En este ejemplo la clase Employee se define como “abstract” lo cual impide crear instancias de esta, es por eso que solo se crean dos tablas, si la base no fuera “abstract” se crearía una tercer tabla para soportar los datos de la entidad base.

 

Definición del Mapping


Para cada entidad derivada se define la clase de mapping donde se especifica el nombre de la tabla.

 

public class EmployeeInternalMap : EntityTypeConfiguration<EmployeeInternal>
{
    public EmployeeInternalMap()
    {

        Map(x =>
        {
            x.MapInheritedProperties();
            x.ToTable("InternalEmployee");
        });

      
    }
}

public class EmployeeExternalMap : EntityTypeConfiguration<EmployeeExternal>
{
    public EmployeeExternalMap()
    {

        Map(x =>
        {
            x.MapInheritedProperties();
            x.ToTable("ExternalEmployee");
        });

        Property(x => x.ConsultantName).IsRequired()
                                       .HasColumnType("varchar")
                                       .HasMaxLength(100);
    }
}

Se destaca del mapping la invocación al método MapInheritedProperties(), lo cual aplica un re-mapping las propiedades de la clase base.

image

 

Problema con la key de la tabla


Si los test los diseñamos en base a los artículos anteriores, al ejecutarlos obtendremos un error con la clave primaria.

SNAGHTML23be7cf7

La causa se debe a que este forma de mapear la herencia no define columnas identity para las claves, por medio de código se tendrá que asegurar el id secuencial que se asigna a la entidad cuando esta se persiste.

Para solucionar este problema se definió en la repositorio un método que nos ayudara a obtener el ultimo id. El método GetLastId() recupera el ultimo utilizado en la combinación de las tablas que definen la herencia.

Se lo declara primeramente en la interfaz.

public interface IEmployeeRepository : IRepository<Employee>
{
    List<EmployeeInternal> GetAllInternalType();

    List<Employee> GetAllExternalType();

    int GetLastId();

}

Y luego en la clase que implementa el repositorio.

 

  public class EmployeeRepository : BaseRepository<Employee>, IEmployeeRepository
  {

      public int GetLastId()
      {
          using (NorthWindContext context = new NorthWindContext())
          {
              int? lastId = context.Employees.Max(x => (int?)x.EmployeeID);

              return lastId.HasValue ? lastId.Value : 0;
          }
      }

  }

 

La ejecución del mismo implica un UNION ALL entre las tablas que deriven de la misma base para luego aplicar el MAX() del campo definido como key

image 

 

Test – Inicializar datos


La inicialización requiere que por cada entidad creada se realice una consulta previa para recuperar el ultimo id

image

En la imagen se observa como a pesar de ser diferentes tablas se mantiene el consecutivo en la key.

 

Test – Obtener todos los empleados


La query generada para recuperar todos los empleados requiere unir las tablas de cada clase derivada.

image

 

Test – Recuperar todos los empleados del tipo interno


Ya sea usando un repositorio especial para el tipo concreto “GetAllInternal_UsingSpecificRepository_Employee()” o definiendo un método en el repositorio base el cual podria utilizar el OfType<>  “GetAllInternal_UsingGenericRepository_Employee()”, el resultado es idéntico, solo realiza la query sobre una tabla en particular donde se define el tipo.

 

image

 

Test – Obtener todos los empleados Externos


Al igual que los test anteriores, hacer uso de un repositorios especifico “GetAllExternal_Employee()” para recuperar los empleados externos o definir un método en el repositorio base que haga uso del “is” para definir que tipo de entidad cargar “GetAllExternal_UsandoIs_Employee()”, resulta en la mismo SELECT.

 

image

 

Resumen


Si bien en algunos casos este tipo de persistencia puede resultar útil, el hecho de no poder definir un campo clave como secuencial puede dificultar su implementación.

La creación de diferentes tablas generan redundancia de campos al duplicarlos, lo cual no lo hace un modelo de persistencia aconsejable, aunque puede que esta sea una característica buscada cuando no se requiere un modelo normalizado.

En resumen, analizar previamente los modelos de herencia TPT y TPH, dejando como ultima alternativa el TPC.

El modelo TPH hace uso de un discriminador para definir los tipos concretos. El TPT, lo realiza mediante la relación entre las tablas. En TPC en cambio la tabla completa define el tipo.

 

Documentación de referencia


Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC)

 

Código


Se utilizo Visual Studio 2012, no se adjunta ninguna base de datos, esta será creada al ejecutar los test utilizando el modelo definido en el contexto de EF

[C#]
 

miércoles, 28 de agosto de 2013

[Entity Framework][Code First] Herencia - Tabla por tipo - Table per Type (TPT)

 

Introducción


Analizaremos un modelo de herencia el cual permite definir una tabla para cada tipo concreto. En este caso la relación entre las tablas define el tipo de instancia de la entidad.

El modelo utilizado en este artículo es idéntico al anterior:

[Entity Framework][Code First] Herencia - Tabla por jerarquía - Table per Hierarchy (TPH)

 

Definición del modelo


Modelaremos la entidad empleado y sus derivados que representan al personal externo y el contratado por la compañía.

Partiremos de un modelo de clases como el siguiente:

image

y obtendremos un modelo de tablas

image

en donde cada la clase base estará en una tabla mientras que los derivados, con sus atributos particulares, en otras distintas.

En este caso la relación entre las tablas define el tipo, a diferencia del modelo TPH (Tabla por jerarquía) en donde un campo era quien lo indicaba.

 

Definición del Mapping


Se va a necesitar definir en que tabla se persiste cada tipo especifico, es por ello que se requiere una clase de mapping para cada derivado que herede de la base.

 

public class NorthWindContext : DbContext
{

    public NorthWindContext()
        : base("NorthwindDb")
    {
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    public DbSet<Employee> Employees { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new EmployeeMap());

        modelBuilder.Configurations.Add(new EmployeeInternalMap());
        modelBuilder.Configurations.Add(new EmployeeExternalMap());

        base.OnModelCreating(modelBuilder);
    }

}

public class EmployeeMap : EntityTypeConfiguration<Employee>
{
    public EmployeeMap()
    {
        HasKey(x => x.EmployeeID);
        Property(x => x.LastName).HasMaxLength(20).IsRequired();
        Property(x => x.FirstName).HasMaxLength(10).IsRequired();

        Property(x => x.Address).HasMaxLength(60);
        Property(x => x.City).HasMaxLength(15);
        Property(x => x.Region).HasMaxLength(15);
        Property(x => x.PostalCode).HasMaxLength(10);
        Property(x => x.Country).HasMaxLength(15);

    }
}

public class EmployeeInternalMap : EntityTypeConfiguration<EmployeeInternal>
{
    public EmployeeInternalMap()
    {
        ToTable("EmployeeInternal");

      
    }
}

public class EmployeeExternalMap : EntityTypeConfiguration<EmployeeExternal>
{
    public EmployeeExternalMap()
    {
        ToTable("EmployeeExternal");

        Property(x => x.ConsultantName).IsRequired()
                                       .HasColumnType("varchar")
                                       .HasMaxLength(100);
    }
}

El principal detalle esta en la definición de los nombre de la tablas

image

 

Definición Repository


Este no sufrió cambios con respecto al articulo anterior donde tratamos TPH.

Nota: se agrego un método adicional a la interfaz del repositorio el cual implementa una query linq que utiliza el “is”, pero no varia la forma de definir esta funcionalidad con respecto al articulo anterior. Se analizara este punto más adelante cuando tratemos el Test que utiliza esta implementacion.

 

Test – Inicializar datos


La inicialización de datos es idéntica al artículo anterior donde explique TPH.

Pero el resultado de los “insert” que define EF son bastante diferentes, ya que por cada entidad creada requiere realiza dos operaciones, uno en la tabla base y otro en cada especialización, esto se aprecia claramente en el trace tomado desde el sql profiler.

image

 

Test – Obtener todos los empleados


Recuperar todos los empleados requiere implementar la unión de dos tablas, la de empleados internos y externos,  al ejecutar el test se puede observar claramente si analizamos el select definido por EF

image

La unión de las dos tablas que representan las entidades hijas se unen mediante un INNER JOIN con la tabla padre de empleados

Aquí nuevamente aparecen los “0X0X” y “0X1X” los cuales son usados por EF para determinar que tipo de instancia debe crear

 

Test – Recuperar todos los empleados de tipo interno, usando un repositorio especifico


Utilizamos el repositorio definido para un tipo concreto, en este caso se recuperan los empleados internos a la empresa

SNAGHTML31ecfb93

Esta acción unirá mediante un INNER JOIN la tabla que representa estos empleados con la tabla base, del cruce se obtendrán solo los registros de un único tipo.

 

image

 

Test – Recuperar todos los empleados del tipo interno, usando funcionalidad del repositorio genérico


Utilizar el método OfType<> definido en un método en al repositorio general

SNAGHTML31f473ce

dará el mismo resultado que utilizar el repositorio especifico del test anterior

image

 

Test – Obtener todos los empleados Externos


Se obtienes todos los empleados de contratación externa.

SNAGHTML32077f62

El join se realiza utilizando la tabla que representa los empleados externos a la compañía.

image

 

Test – Obtener todo los empleados externos, usando el “is”


Aplicaremos una variación en la técnica utilizada al definir el tipo que queremos filtrar, para ello nos ayudaremos con el “is” en el where de la query linq.

SNAGHTML321b1bf0

como veremos EF detecta el “is” como la acción de recuperar un tipo especifico, por eso el sql que define nuevamente hace uso del JOIN entre las tablas

image

 

Documentación de referencia


Inheritance with EF Code First: Part 2 – Table per Type (TPT)

 

Código


Se utilizo Visual Studio 2012, no se adjunta ninguna base de datos, esta será creada al ejecutar los test utilizando el modelo definido en el contexto de EF

[C#]
 

lunes, 26 de agosto de 2013

[Entity Framework][Code First] Herencia - Tabla por jerarquía - Table per Hierarchy (TPH)

 

Introducción


Una de las principales ventajas al implementar un ORM, en nuestro caso de la mano de Entity Framework, apunta a tener a nuestra disposición todo el poder de la Programación Orientada a Objeto (POO) para modelar nuestras entidades de negocio.

La Herencia es una de las practicas mas utilizadas para modelar entidades, pudiendo representar en un modo realista el diseño del negocio, mejor aun si le unimos un fácil mapeo de las entidades con la estructura de la base de datos.

Existen tres formas de mapear una estructura de Herencia con tablas:

  1. Tabla por jerarquía - Table per Hierarchy (TPH)
  2. Tabla por tipo - Table per Type (TPT)
  3. Tabla por tipo concreto - Table per Concrete Type (TPC)

 

En este articulo tratare el primero de ellos, Tabla por jerarquía, en este las diferentes clases que definen la herencia mapean contra una única tabla en la base de datos utilizando un campo discriminador para determinar el tipo especifico.

 

Definición del modelo


En el ejemplo definiremos una entidad Empleado pudiendo encontrarse dos tipo: los empleados internos de la empresa y los de contratación externa.

 

public abstract class Employee
{
    public int EmployeeID { get; set; }

    public string LastName { get; set; }
    public string FirstName { get; set; }

    public string Address { get; set; }
    public string City { get; set; }
    public string Region { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }

}

public class EmployeeExternal : Employee
{
    public string ConsultantName { get; set; }

    public DateTime? ContactExpiration { get; set; }
}

public class EmployeeInternal : Employee
{
    public DateTime? HireDate { get; set; }
}

 

En al definición de las clases pueden observarse dos detalles:

  1. la clase base se define como abstract, este impedirá crear instancias del tipo base, con lo cual se obliga a crear instancias solo de los tipos derivados.
  2. las clases hijas poseen propiedades concretas para cada tipo que definen características determinadas, es recomendable que las propiedades de las clases derivadas permitan nulos o sino asignarle un valor por default.

La idea es poder mapear el modelo de objetos como el siguiente

image

generando una tabla como ser

image

 

Definición del Mapping


Definir como se debe persistir este tipo de modelo es bastante simple y no difiere a lo ya aprendido en los anteriores artículos que realice sobre el tema.

Se define el contexto de EF

public class NorthWindContext : DbContext
{

    public NorthWindContext()
        : base("NorthwindDb")
    {
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    public DbSet<Employee> Employees { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new EmployeeMap());

        base.OnModelCreating(modelBuilder);
    }

}

public class EmployeeMap : EntityTypeConfiguration<Employee>
{
    public EmployeeMap()
    {
        HasKey(x => x.EmployeeID);
        Property(x => x.LastName).HasMaxLength(20).IsRequired();
        Property(x => x.FirstName).HasMaxLength(10).IsRequired();

        Property(x => x.Address).HasMaxLength(60);
        Property(x => x.City).HasMaxLength(15);
        Property(x => x.Region).HasMaxLength(15);
        Property(x => x.PostalCode).HasMaxLength(10);
        Property(x => x.Country).HasMaxLength(15);

        Map<EmployeeInternal>(x => x.Requires("Type")
                                        .HasValue("I")
                                        .HasColumnType("char")
                                        .HasMaxLength(1));

        Map<EmployeeExternal>(x => x.Requires("Type")
                                        .HasValue("E"));

    }
}

 

Para la definición de la  herencia se debe puntualizar estas líneas:

image

En ellas se define el nombre del campo que actuara como discriminador del tipo, así como los valores que tomara para cada clase hija definida, opcionalmente se puede especificar el tipo y precisión de la columna.

Sino se especifica el tipo para la columna del discriminador Entity Framework usara valores por defecto, por lo que la columna podrías crearse como nvarchar(128), esto puede resultar de poca importancia, pero si solo se va a contener un único carácter se estaría desperdiciando espacio para ese campo.

El campo definido como discriminador no se define como propiedad en las clases de la entidad de dominio, ya que la propia instancia de la clase define el tipo en si mismo.

 

Definición Repository


La definición del repositorio tiene algunas novedades respecto a los artículos anteriores, en este caso junto al proyecto que de entidades se define la interfaz para poder extender el repositorio.

Quizás en este momento no se aprecie este tipo de implementación, pero si se hace uso de algún framework de IoC (Invertion of Control) como ser Ninject, Unity, etc, allí si se requieren interfaces para poder desacoplar la creación del repositorio concreto.

 

public interface IEmployeeRepository : IRepository<Employee>
{
    List<EmployeeInternal> GetAllInternalType();
}

 

public class EmployeeRepository : BaseRepository<Employee>, IEmployeeRepository
{
    /// <summary>
    /// Retorna todos los empleados externos a la empresa
    /// </summary>
    /// <returns></returns>
    public List<EmployeeInternal> GetAllInternalType()
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            return context.Employees.OfType<EmployeeInternal>().ToList();
        }
    }

}

public class EmployeeInternalRepository : BaseRepository<EmployeeInternal>
{

}

public class EmployeeExternalRepository : BaseRepository<EmployeeExternal>
{

}

Test – Inicializar datos


En si mismo la inicialización de los datos no son un test, pero como todos los test harán uso de un mismo conjunto de datos se podría decir que la inicialización también aplica pruebas en al creación de las entidades.

La ejecución de esta inicialización implica la validación de los métodos de creación de las entidades.

 

private void InitializeTestData()
{
    IEmployeeRepository repoEmployee = new EmployeeRepository();
    
    //
    // elimino registros previos
    //
    List<Employee> list = repoEmployee.GetAll();

    list.ForEach(x => repoEmployee.Delete(x));


    //
    // creo un empleado interno
    //
    employee1 = new EmployeeInternal()
    {
        FirstName = "name1",
        LastName = "lastname1",
        HireDate = DateTime.Now.AddMonths(-10)
    };
    repoEmployee.Create(employee1);

    //
    // creo un empleado externo
    //
    employee2 = new EmployeeExternal()
    {
        FirstName = "name2",
        LastName = "lastname2",
        ConsultantName = "ConsultantName2",
        ContactExpiration = DateTime.Now.AddYears(2)
    };
    repoEmployee.Create(employee2);

    //
    // creo otro empleado externo
    //
    employee3 = new EmployeeExternal()
    {
        FirstName = "name3",
        LastName = "lastname3",
        ConsultantName = "ConsultantName3",
        ContactExpiration = DateTime.Now.AddYears(1)
    };
    repoEmployee.Create(employee3);
}

La inicialización de los datos ejecuta instrucciones sql donde se pueden observar el campo definido como discriminador

image

 

Test – Obtener todos los empleados


Obtendremos la lista de todos los empleados pudiendo validar el tipo de cada uno de ellos.

 

[TestMethod]
public void GetAll_Employee()
{
    InitializeTestData();

    //
    //recupero todos los empleados
    //
    IEmployeeRepository repoEmployee = new EmployeeRepository();

    List<Employee> listIntEmployee = repoEmployee.GetAll();

    //
    // Assert
    //
    Assert.AreEqual(listIntEmployee.Count, 3);

    Assert.IsInstanceOfType(listIntEmployee[0], typeof(EmployeeInternal));
    Assert.IsInstanceOfType(listIntEmployee[1], typeof(EmployeeExternal));
    Assert.IsInstanceOfType(listIntEmployee[2], typeof(EmployeeExternal));

}

image

El query creado para recuperar todos los empleados incluye un filtro que especifica todos los tipos existentes.

 

Test – Recuperar todos los empleados de tipo interno, usando un repositorio especifico


Se recuperan las entidades que corresponden a empleados propios de la empresa, pero para lograrlo se hace uso del repositorio definido para ese tipo concreto.

Se define un repositorio concreto para la clase EmployeeInternal, pudiendo utilizar los métodos que define el RepositorioBase<>

 

[TestMethod]
public void GetAllInternal_UsingSpecificRepository_Employee()
{
    InitializeTestData();

    //
    //recupero solo empleados internos
    //
    IRepository<EmployeeInternal> repoInternalEmployee = new EmployeeInternalRepository();

    List<EmployeeInternal> listIntEmployee = repoInternalEmployee.GetAll();

    //
    // Assert
    //
    Assert.AreEqual(listIntEmployee.Count, 1);

    Assert.IsInstanceOfType(listIntEmployee[0], typeof(EmployeeInternal));

    Assert.AreEqual(listIntEmployee[0].FirstName, employee1.FirstName);
    Assert.IsNotNull(listIntEmployee[0].HireDate);
    Assert.AreEqual(listIntEmployee[0].HireDate.Value.ToShortDateString(), employee1.HireDate.Value.ToShortDateString());

}

image

Al recuperar todas las instancia para un tipo en concreto la query filtra por el identificado definido para ese tipo.

Seguramente se preguntaran que significa el

'0X0X' AS [C1],

esa línea es usada internamente por Entity Framework para saber la instancia de que tipo en concreto tiene que materializar, o sea es una marca que define el tipo.

Test – Recuperar todos los empleados del tipo interno, usando funcionalidad del repositorio genérico


Se recuperan las entidades que corresponden a empleados propios de la empresa, pero en este caso se utilizara el repositorio definido para la clase base.

Es por medio del OfType<> que se especifica que tipo concreto se quiere recuperar.

 

[TestMethod]
public void GetAllInternal_UsingGenericRepository_Employee()
{

    InitializeTestData();

    //
    //recupero solo empleados internos
    //
    IEmployeeRepository repoEmployee = new EmployeeRepository();

    List<EmployeeInternal> listIntEmployee = repoEmployee.GetAllInternalType();

    //
    // Assert
    //
    Assert.AreEqual(listIntEmployee.Count, 1);

    Assert.IsInstanceOfType(listIntEmployee[0], typeof(EmployeeInternal));

    Assert.AreEqual(listIntEmployee[0].FirstName, employee1.FirstName);
    Assert.IsNotNull(listIntEmployee[0].HireDate);
    Assert.AreEqual(listIntEmployee[0].HireDate.Value.ToShortDateString(), employee1.HireDate.Value.ToShortDateString());

}

image

Usar el OfType<> genera el mismo resultado que especializar el repositorio, la query generada son idénticas

 

Test – Obtener todos los empleados Externos


Se obtienes todos los empleados de contratación externa.

 

[TestMethod]
public void GetAllExternal_Employee()
{

    InitializeTestData();

    //
    //recupero solo empleados externos
    //
    IRepository<EmployeeExternal> repoExternalEmployee = new EmployeeExternalRepository();

    List<EmployeeExternal> listExtEmployee = repoExternalEmployee.GetAll();

    //
    // Assert
    //
    Assert.AreEqual(listExtEmployee.Count, 2);

    Assert.IsInstanceOfType(listExtEmployee[0], typeof(EmployeeExternal));
    Assert.IsInstanceOfType(listExtEmployee[1], typeof(EmployeeExternal));

    Assert.AreEqual(listExtEmployee[0].FirstName, employee2.FirstName);
    Assert.IsNotNull(listExtEmployee[0].ContactExpiration);
    Assert.AreEqual(listExtEmployee[0].ContactExpiration.Value.ToShortDateString(), employee2.ContactExpiration.Value.ToShortDateString());
    Assert.AreEqual(listExtEmployee[0].ConsultantName, employee2.ConsultantName);

    Assert.AreEqual(listExtEmployee[1].FirstName, employee3.FirstName);
    Assert.IsNotNull(listExtEmployee[1].ContactExpiration);
    Assert.AreEqual(listExtEmployee[1].ContactExpiration.Value.ToShortDateString(), employee3.ContactExpiration.Value.ToShortDateString());
    Assert.AreEqual(listExtEmployee[1].ConsultantName, employee3.ConsultantName);
}

image

 

 

Poder crear instancias de la clase base


Al comienzo del artículo comente que la clase base se define con abstract para así forzar siempre usar las derivadas, al generar la tabla el campo que actúa como discriminador no permita nulo.

Ahora si queremos crear instancias de la clase base, solo será cuestión de permitirlo quitando el abstract.

 

image

la tabla generada por EF ahora permite nulo en el campo “Type”

image

con lo cual se podrán crear instancias del tipo base “Employee”, para probarlo creamos un test

[TestMethod]
public void GetAll_WithBaseType_Employee()
{
    IEmployeeRepository repoEmployee = new EmployeeRepository();

    //
    // creo un empleado interno
    //
    EmployeeInternal employee1 = new EmployeeInternal()
    {
        FirstName = "name1",
        LastName = "lastname1",
        HireDate = DateTime.Now.AddMonths(-10)
    };
    repoEmployee.Create(employee1);

    //
    // creo un empleado interno
    //
    Employee employee2 = new Employee()
    {
        FirstName = "name2",
        LastName = "lastname2"
    };
    repoEmployee.Create(employee2);

    //
    //recupero todos los empleados
    //

    List<Employee> listIntEmployee = repoEmployee.GetAll();

    //
    // Assert
    //
    Assert.AreEqual(listIntEmployee.Count, 2);

    Assert.IsInstanceOfType(listIntEmployee[0], typeof(EmployeeInternal));
    //validamos los tipos base de cada objeto recuperado
    Assert.AreEqual(listIntEmployee[0].GetType().BaseType, typeof(Employee));
    Assert.AreEqual(listIntEmployee[1].GetType().BaseType, typeof(object));

}

Al final del test se valida los tipo base de cada instancia, para el empleado interno será la clase “Employee”, pero para una instancia base del empleado al no derivar de ninguna otra será el tipo “object”.

El query generado en este caso es bastante mas complejo

image

por eso de ser posible definir la herencia para usar solo las clases hijas

 

Campos discriminador numérico


Además de definir un campo discriminador del tipo string o char, también se puede definir numérico, solo hay que especificar los valores en el mapping

image

la creación de la tabla cambiara el campo “Type” como numérico

image

 

Documentación de referencia


Inheritance with EF Code First: Part 1 – Table per Hierarchy (TPH)

 

Código


Se utilizo Visual Studio 2012, no se adjunta ninguna base de datos, esta será creada al ejecutar los test utilizando el modelo definido en el contexto de EF

[C#]