Entity Framework 4 CodeOnly - Table per concrete Class (TPC)

En el último post acerca de le herencia en Entity Framework 4 y Code Only, vamos a ver la estrategia de Table per concrete Class (TPC).

La diferencia con las estrategias de los ejemplos anteriores: Table per Hierarchy y Table per Type es que, en este caso, la clase base no tiene ningún tipo de representación en las base de datos y sólo tiene sentido a nivel de clases del modelo.

A continuación vemos como tenemos definido una tabla por subtipo:

TablaTpC

Ahora vamos a definir los mapeos de la base de datos con nuestras clases.

Para el subtipo Book:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class BooksTpCConfiguration : EntityConfiguration<Book>
{
    public BooksTpCConfiguration()
    {
        this
            .HasKey(b => b.Id)
            .MapSingleType(
                b => new
                {
                    Id = b.Id,
                    Name = b.Name,
                    Price = b.Price,
                    Pages = b.PageNumber,
                    Author = b.Author
                }
            )
            .ToTable("BooksTpC");
    }
}
Para el subtipo

Television:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TelevisionsTpCConfiguration : EntityConfiguration<Television>
{
    public TelevisionsTpCConfiguration()
    {
        this
            .HasKey(t => t.Id)
            .MapSingleType(
                t => new
                {
                    Id = t.Id,
                    Name = t.Name,
                    Price = t.Price,
                    Size = t.Size,
                    Brand = t.Brand,
                }
            )
            .ToTable("TelevisionsTpC");
    }
}

Para el subtipo Shirt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ShirtsTpCConfiguration : EntityConfiguration<Shirt>
{
    public ShirtsTpCConfiguration()
    {
        this
            .HasKey(s => s.Id)
            .MapSingleType(
                s => new
                {
                    Id = s.Id,
                    Name = s.Name,
                    Price = s.Price,
                    Colour = s.Colour,
                    Size = s.Size,
                }
            )
            .ToTable("ShirtsTpC");
    }
}

Vemos que mediante la expresión MapSingleType cada clase mapea las propiedades propias del subtipo y de la clase base.

Para crear las instancias a las que añadimos nuestros objetos utilizaremos una clase que herede de ObjectContext. En ella definimos sólo los subtipos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ModelTpCContext : ObjectContext
{
    public ModelTpCContext(EntityConnection connection)
        : base(connection)
    {
        DefaultContainerName = "ModelTpCContext";
    }
    public IObjectSet<Book> Books
    {
        get { return base.CreateObjectSet<Book>(); }
    }
    public IObjectSet<Television> Televisions
    {
        get { return base.CreateObjectSet<Television>(); }
    }
    public IObjectSet<Shirt> Shirts
    {
        get { return base.CreateObjectSet<Shirt>(); }
    }
}

Y para terminar con el ejemplo, vemos el método que configura y persiste los 3 subtipos tratados:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public void AddProducts()
{
    // crear la conexión a la BBDD y crear el contexto de EF
    SqlConnection conn = new SqlConnection("Data Source=(local);Initial Catalog=Serrate.CodeOnly;Integrated Security=True;MultipleActiveResultSets=True;");
    var builder = new ContextBuilder<ModelTpCContext>();
    // debemos añadir las 3 configuraciones que equivalen a cada tabla
    builder.Configurations.Add(new BooksTpCConfiguration());
    builder.Configurations.Add(new TelevisionsTpCConfiguration());
    builder.Configurations.Add(new ShirtsTpCConfiguration());
    var context = builder.Create(conn);
    // añadimos todas las entidades a Products
    var book = new Book()
    {
        Id = Guid.NewGuid(),
        Name = "Clash of Kings",
        Price = 60.30M,
        PageNumber = 489,
        Author = "George R. R. Martin"
    };
    context.Books.AddObject(book);
    var tv = new Television()
    {
        Id = Guid.NewGuid(),
        Name = "Led TV",
        Price = 999.99M,
        Brand = "Samsung",
        Size = 40
    };
    context.Televisions.AddObject(tv);
    var shirt = new Shirt()
    {
        Id = Guid.NewGuid(),
        Name = "Kamtxatka Shirt",
        Price = 20.50M,
        Colour = 3,
        Size = "XL"
    };
    context.Shirts.AddObject(shirt);
    // guardamos los cambios
    context.SaveChanges();
}