left-icon

Entity Framework Code First Succinctly®
by Ricardo Peres

Previous
Chapter

of
A
A
A

CHAPTER 2

Domain Model

Domain Model


Scenario

Let’s consider the following scenario as the basis for our study.

The domain model

  1. The domain model

You will find all these classes in the accompanying source code. Let’s try to make some sense out of them:

  • A Customer has a number of Projects.
  • Each Project has a collection of ProjectResources, belongs to a single Customer, and has a ProjectDetail with additional information.
  • A ProjectDetail refers to a single Project.
  • A ProjectResource always points to an existing Resource and is assigned to a Project with a given Role.
  • A Resource knows some Technologies and can be involved in several Projects.
  • A Technology can be collectively shared by several Resources.
  • Both Customers and Resources have Contact information.

Note: You can find the full source code in the following Git repository: https://bitbucket.org/syncfusiontech/entity-framework-code-first-succinctly/overview.

Core Concepts

Before a class model can be used to query a database or to insert values into it, Entity Framework needs to know how it should translate code (classes, properties, and instances) back and forth into the database (specifically, tables, columns and records). For that, it uses a mapping, for which two APIs exist. More on this later, but first, some fundamental concepts.

Contexts

A context is a class that inherits from DbContext and which exposes a number of entity collections in the form of DbSet<T> properties. Nothing prevents you from exposing all entity types, but normally you only expose aggregate roots, because these are the ones that make sense querying on their own.

An example context might be the following.

public class ProjectsContext : DbContext

{

  public DbSet<Tool> Tools { get; set; }

  public DbSet<Resource> Resources { get; set; }

  public DbSet<Project> Projects { get; set; }

  public DbSet<Customer> Customers { get; set; }

  public DbSet<Technology> Technologies { get; set; }

}

Tip: Do notice that both setters and getters for the entity collections are public.

Note: Feel free to add your own methods, business or others, to the context class.

The DbContext class offers a number of constructors, mostly for configuring the connection string:

  • If the no-arguments constructor is called, the DbContext will assume that a connection string with the same name as the context’s class will exist in the configuration file.
  • There’s also a constructor that takes a single string parameter. This parameter will either be a full connection string that is specific to the current database provider or a name of a connection string that must be present in the configuration file.
  • For the sake of completeness, another constructor exists that takes an existing DbConnection; Entity Framework might not take full control of this connection, for example, it won’t try to dispose of it when no longer needed.

public class ProjectsContext : DbContext

{

  public ProjectsContext() { }

  public ProjectsContext(String nameOrConnectionString): base(nameOrConnectionString)

  { }

  public ProjectsContext(DbConnection existingConnection, Boolean contextOwnsConnection): base(existingConnection, contextOwnsConnection)

  { }

}

If we use the constructor overload that takes a connection string by its name, we must use the format “Name=Some Name”.

public class ProjectsContext : DbContext

{

  public ProjectsContext(String name) : base("Name=AnotherName") { }

}

Entities

At the very heart of the mapping is the concept of entity. An entity is just a class that is mapped to an Entity Framework context and which has an identity, or a property that uniquely identifies instances of it. In DDD parlance, it is said to be an aggregate root if it is meant to be directly queried, think of a Project or a Customer, or an entity if it is loaded together with an aggregate root and not generally considerable on its own, such as project details or customer address. An entity is persisted on its own table and may have any number of business or validation methods.

public class Project

{

  public Project()

  {

    this.ProjectResources = new HashSet<ProjectResource>();

  }

 

  public Int32 ProjectId { get; set; }

 

  public String Name { get; set; }

 

  public DateTime Start { get; set; }

 

  public DateTime? End { get; set; }

 

  public virtual ProjectDetail Detail { get; set; }

 

  public virtual Customer Customer { get; set; }

  public void AddResource(Resource resource, Role role)

  {

    resource.ProjectResources.Add(new ProjectResource() 

    { Project = this, Resource = resource, Role = role });

  }

 

  public Resource ProjectManager

  {

    get

    {

      return (this.ProjectResources.ToList()

      .Where(x => x.Role == Role.ProjectManager)

      .Select(x => x.Resource).SingleOrDefault());

    }

  }

 

  public IEnumerable<Resource> Developers

  {

    get

    {

      return (this.ProjectResources.Where(x => x.Role == Role.Developer)

      .Select(x => x.Resource).ToList());

    }

  }

 

  public IEnumerable<Resource> Testers

  {

    get

    {

      return (this.ProjectResources.Where(x => x.Role == Role.Tester)

      .Select(x => x.Resource)).ToList();

    }

  }

 

  public virtual ICollection<ProjectResource> ProjectResources { get; protected set;

  }

 

  public override String ToString()

  {

    return (this.Name);

  }

}

Here you can see some patterns that we will be using throughout the book:

  • An entity needs to have at least a public parameter-less constructor.
  • An entity always has an identifier property, which has the same name and ends with Id.
  • Collections are always generic, have protected setters, and are given a value in the constructor in the form of an actual collection (like HashSet<T>).
  • Calculated properties are used to expose filtered sets of actually persisted properties.
  • Business methods are used for enforcing business rules.
  • A textual representation of the entity is supplied by overriding ToString.

A domain model where its entities have only properties (data) and no methods (behavior) is sometimes called an anemic domain model. You can find a good description for this anti-pattern on Martin Fowler’s web site: http://www.martinfowler.com/bliki/AnemicDomainModel.html.

Complex Types

A complex type is also a class with some properties and maybe methods, but unlike an entity, it doesn’t have an identity property and doesn’t have its own table for persistence. Instead, its properties are saved into the same table as its declaring type. A complex type is useful for grouping properties that conceptually should always appear together, such as the city, country, street, and zip code in an address. By reusing complex types, we can have the same logic repeated in different entities. Both a customer and a human resource might have contact information with the same structure:

public class ContactInformation

{

  public String Email { get; set; }

 

  public String Phone { get; set; }

}

public class Resource

{

  public ContactInformation Contact { get; set; }

}

public class Customer

{

  public ContactInformation Contact  { get; set; }

}

Complex types have the following limitations:

  1. They cannot have navigation properties (references or collections, see next topics).
  2. They cannot be null, or their containing entity must initialize them.
  3. They cannot point to their containing entity.

Scalar Properties

Scalars are simple values, like strings, dates, and numbers. They are where actual entity data is stored, and can be of one of any of these types.

Table 1: Scalar Properties

.NET Type

SQL Server Type

Description

Boolean

BIT

Single bit.

Byte

TINYINT

Single byte (8 bits).

Char

CHAR,

NCHAR

ASCII or UNICODE char (8 or 16 bits).

Int16

SMALLINT

Short integer (16 bits).

fInt32

INT

Integer (32 bits).

Int64

BIGINT

Long (64 bits).

Single

REAL

Floating point number (32 bits).

Double

FLOAT

Double precision floating point number (64 bits).

Decimal

MONEY,

SMALLMONEY

Currency (64 bits) or small currency (32 bits).

Guid

UNIQUEIDENTIFIER

Globally Unique Identifier (GUID).

DateTime

DATE,

DATETIME,

SMALLDATETIME,

DATETIME2

Date with or without time.

DateTimeOffset

DATETIMEOFFSET

Date and time with timezone information.

TimeSpan

TIME

Time.

String

VARCHAR,

NVARCHAR,

XML

ASCII (8 bits per character), UNICODE (16 bits) or XML character string. Can also represent a Character Long Object (CLOB).

Byte[]

BINARY,

VARBINARY

,

ROWVERSION

Binary Large Object (BLOB).

Enum

INT

Enumerated value.

DbGeography

GEOGRAPHY

Geography spatial type.

DbGeometry

GEOMETRY

Planar spatial type.

The types Byte, Char, and String can have a maximum length specified. A value of -1 translates to MAX.

All scalar types can be made nullable, meaning they might have no value set. In the database, this is represented by a NULL column.

Scalar properties need to have both a getter and a setter, but the setter can have a more restricted visibility than the getter: internal, protected internal or protected.

Some examples of scalar properties are as follows.

public class Project

{

  public Int32 ProjectId { get; set; }

 

  public String Name { get; set; }

  public DateTime Start { get; set; }

 

  public DateTime? End { get; set; }

}

Identity Properties

One or more of the scalar properties of your entity must represent the underlying table’s primary key, which can be single or composite.

Primary key properties can only be of any of the basic types, which is any type in the list above except arrays and enumerations, but no complex types or other entity’s types.

References

A reference from an entity to another defines a bidirectional relation. There are two types of reference relations:

  • Many-to-one: several instances of an entity can be associated with the same instance of another type (such as projects that are owned by a customer).

Many-to-one relationship

  1. Many-to-one relationship
  • One-to-one: an instance of an entity is associated with another instance of another entity; this other instance is only associated with the first one (such as a project and its detail).

One-to-one relationship

  1. One-to-one relationship

In EFCF, we represent an association by using a property of the other entity’s type.

one-to-one, many-to-one

  1. References: one-to-one, many-to-one

We call an entity’s property that refers to another entity as an endpoint of the relation between the two entities.

public class Project

{

  //one endpoint of a many-to-one relation

  public virtual Customer Customer { get; set; }

  //one endpoint of a one-to-one relation

  public virtual ProjectDetail Detail { get; set; }

}

public class ProjectDetail

{

  //the other endpoint of a one-to-one relation

  public Project Project { get; set; }

}

public class Customer

{

  //the other endpoint of a many-to-one relation

  public virtual ICollection<Project> Projects { get; protected set; }

}

Note: By merely looking at one endpoint, we cannot immediately tell what its type is (one-to-one or many-to-one), we need to look at both endpoints.

Collections

Collections of entities represent one of two possible types of bidirectional relations:

  • One-to-many: a single instance of an entity is related to multiple instances of some other entity’s type (such as a project and its resources).

One-to-many relationship

  1. One-to-many relationship
  • Many-to-many: possibly a number of instances of a type can be related with any number of instances of another type (such as resources and the technologies they know).

Many-to-many relationship

  1. Many-to-many relationship

one-to-many, many-to-many

  1. Collections: one-to-many, many-to-many

Entity Framework only supports declaring collections as ICollection<T> (or some derived class or interface) properties. In the entity, we should always initialize the collections properties in the constructor.

public class Project

{

  public Project()

  {

    this.ProjectResources = new HashSet<ProjectResource>();

  }

  public virtual ICollection<ProjectResource> ProjectResources

  { get; protected set; }

}

Note: References and collections are collectively known as navigation properties, as opposed to scalar properties.

Mapping by Attributes

Overview

Probably the most used way to express our mapping intent is to apply attributes to properties and classes. This has the advantage that, by merely looking at a class, one can immediately infer its database structure.

Schema

Unless explicitly set, the table where an entity type is to be stored is determined by a convention (more on this later on), but it is possible to set the type explicitly by applying a TableAttribute to the entity’s class.

[Table("MY_SILLY_TABLE", Schema = "dbo")]

public class MySillyType { }

The Schema property is optional and should be used to specify a schema name other than the default. A schema is a collection of database objects (tables, views, stored procedures, functions, etc.) in the same database. In SQL Server, the default schema is dbo.

For controlling how a property is stored (column name, physical order, and database type), we apply a ColumnAttribute.

[Column(Order = 2, TypeName = "VARCHAR")]

public String Surname { get; set; }

[Column(Name = "FIRST_NAME", Order = 1, TypeName = "VARCHAR")]

public String FirstName { get; set; }

If the TypeName is not specified, Entity Framework will use the engine’s default for the property type. SQL Server will use NVARCHAR for String properties, INT for Int32, BIT for Boolean, etc. We can use it for overriding this default.

The Order applies a physical order to the generated columns that might be different from the order by which properties appear on the class. When the Order property is used, there should be no two properties with the same value in the same class.

Marking a scalar property as required requires the usage of the RequiredAttribute.

[Required]

public String Name { get; set; }

Tip: When this attribute is applied to a String property, it not only prevents the property from being null, but also from taking an empty string.

Tip: For value types, the actual property type should be chosen appropriately. If the column is non-nullable, one should not choose a property type that is nullable, such as Int32?.

For a required associated entity, it is exactly the same.

[Required]

public Customer Customer { get; set; }

Setting the maximum allowed length of a string column is achieved by means of the MaxLengthAttribute.

[MaxLength(50)]

public String Name { get; set; }

The MaxLengthAttribute can be also used to set a column as being a CLOB, a column containing a large amount of text. SQL Server uses the types NVARCHAR(MAX) and VARCHAR(MAX). For that, we pass a length of -1.

[MaxLength(-1)]

public String LargeText { get; set; }

It can also be used to set the size of a BLOB (in SQL Server, VARBINARY) column.

[MaxLength(-1)]

public Byte[] Picture { get; set; }

Like in the previous example, the -1 size will effectively be translated to MAX.

Ignoring a property, having Entity Framework never consider it for any operations, is as easy as setting a NotMappedAttribute on the property.

[NotMapped]

public String MySillyProperty { get; set; }

Fully ignoring a type, including any properties that might refer to it, is also possible by applying the NotMappedAttribute to its class instead.

[NotMapped]

public class MySillyType { }

Primary Keys

While database tables strictly don’t require a primary key, Entity Framework requires it. Both single column as well as multi-column (composite) primary keys are supported. Marking a property, or properties, as the primary key is achieved by applying a KeyAttribute.

[Key]

public Int32 ProductId { get; set; }

If we have a composite primary key, we need to apply a ColumnAttribute as well. In it we need to give an explicit order by means of the Order property so that EF knows when an entity is loaded by the Find method, which argument refers to which property.

[Key]

[Column(Order = 1)]

public Int32 ColumnAId { get; set; }

[Key]

[Column(Order = 2)]

public Int32 ColumnBId { get; set; }

Primary keys can also be decorated with an attribute that tells Entity Framework how keys are to be generated (by the database or manually). This attribute is DatabaseGeneratedAttribute, and its values are explained in further detail in the section Identifier Strategies.

Navigation Properties

We typically don’t need to include foreign keys in our entities; instead, we use references to the other entity, but we can have them as well. That’s what the ForeignKeyAttribute is for.

public virtual Customer Customer { get; set; }

[ForeignKey("Customer")]

public Int32 CustomerId { get; set; }

The argument to ForeignKeyAttribute is the name of the navigation property that the foreign key relates to.

Now suppose we have several relations from an entity to the other. For example, a customer might have two collections of projects: one for the current and other for the past projects. It could be represented in code as this.

public partial class Customer

{

  //the other endpoint will be the CurrentCustomer

  [InverseProperty("CurrentCustomer")]

  public virtual ICollection<Project> CurrentProjects { get; protected set; }

  //the other endpoint will be the PastCustomer

  [InverseProperty("PastCustomer")]

  public virtual ICollection<Project> PastProjects { get; protected set; }

}

public partial class Project

{

  public virtual Customer CurrentCustomer { get; set; }

  public virtual Customer PastCustomer { get; set; }

}

In that case, it is impossible for EF to figure out which property should be the endpoint for each of these collections, hence the need for the InversePropertyAttribute. When applied to a collection navigation property, it tells Entity Framework what is the name of the other endpoint’s reference property that will point back to it.

Note: When configuring relationships, you only need to configure one endpoint.

Computed Columns

Entity Framework Code First does not support generating computed columns—columns whose values are not physically stored in a table but instead come from SQL formulas—automatically, but you can do it manually and have them mapped to your entities. A typical case is combining a first and a last name into a full name, which can be achieved on SQL Server very easily.

Computed columns

  1. Computed columns

Another example of a column that is generated on the database is when we use a trigger for generating its values. You can map server-generated columns to an entity, but you must tell Entity Framework that this property is never to be inserted. For that we use the DatabaseGeneratedAttribute with the option DatabaseGeneratedOption.Computed.

public virtual String FirstName { getset; }

 

public virtual String LastName { getset; }

 

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

public virtual String FullName { get protected set; }

Since the property will never be set, we can have the setter as a protected method, and we mark it as DatabaseGeneratedOption.Computed to let Entity Framework know that it should never try to INSERT or UPDATE this column.

With this approach, you can query the FullName computed property with both LINQ to Objects as well as LINQ to Entities.

//this is executed by the database

var me = ctx.Resources.SingleOrDefault(x => x.FullName == "Ricardo Peres");

//this is executed by the process

var me = ctx.Resources.ToList().SingleOrDefault(x => x.FullName == "Ricardo Peres");

Complex Types

Complex types should be decorated with the ComplexTypeAttribute as in the following.

[ComplexType]

public class ContactInformation

{

  public String Email { get; set; }

 

  public String Phone { get; set; }

}

External Metadata

If you have a situation where you can’t change an entity’s code, the entity could have been generated automatically from a tool. See Generating Code Automatically for some examples of this. There’s still something that you can do, provided the entities are generated as partial classes. The MetadataTypeAttribute allows us to have a class that contains the metadata—including, in this case, mapping attributes—for another class. Here’s a quick example.

//the original Project class

public partial class Project

{

  //the following attributes will come from the ProjectMetadata class

  /*[Required]

  [MaxLength(50)]*/

  public String Name { get; set; }

}

//the new declaration

[MetadataType(typeof(ProjectMetadata))]

public partial class Project

{

  sealed class ProjectMetadata

  {

    [Required]

    [MaxLength(50)]

    public String Name { get; set; }

  }

}

This second class declaration is created in a new file and must reside in the same namespace as the original one. We don’t need—in fact, we can’t—to add any of its properties or methods; it will just serve as a placeholder for the MetadataTypeAttribute declaration. In the inner class ProjectMetadata, we will declare any properties for which we want to apply attributes.

Note: This is a good workaround for when code is generated automatically, provided all classes are generated as partial.

Limitations

As of the current version of EFCF, there are some mapping concepts that cannot be achieved with attributes:

For these, we need to resort to code configuration, which is explained next.

Mapping by Code

Overview

Convenient as attribute mapping may be, it has some drawbacks:

  • We need to add references in our domain model to the namespaces and assemblies where the attributes are defined (such as “domain pollution”).
  • We cannot change things dynamically; attributes are statically defined and cannot be overwritten.
  • There isn’t a centralized location where we can enforce our own conventions.

To help with these limitations, Entity Framework Code First offers an additional mapping API: code or fluent mapping. All functionality of the attribute-based mapping is present and more. Let’s see how we implement the most common scenarios.

Fluent, or code, mapping is configured on an instance of DbModelBuilder and normally the place where we can access one is in the OnModelCreating method of the DbContext.

public class ProjectsContext : DbContext

{

  protected override void OnModelCreating(DbModelBuilder modelBuilder)

  {

    //configuration goes here

    base.OnModelCreating(modelBuilder);

  }

}

This infrastructure method is called by Entity Framework when it is initializing a context, after it has automatically mapped whatever entity classes are referenced as DbSet<T> collections or referenced through them.

Schema

Here’s how to configure the entity mappings by code.

//set the table and schema

modelBuilder.Entity<Project>().ToTable("project""dbo");

//ignoring an entity and all properties of its type

modelBuilder.Ignore<Project>();

This is an example of mapping individual properties. Notice how the API allows chaining multiple calls together by, in this case, setting simultaneously the column name, type, maximum length, and required flag. This is very useful and renders the code in a more readable way.

//ignore a property

modelBuilder.Entity<Project>().Ignore(x => x.SomeProperty);

//set a property’s values (column name, type, length, nullability)

modelBuilder.Entity<Project>().Property(x => x.Name).HasColumnName("NAME")

.HasColumnType("VARCHAR").HasMaxLength(50).IsRequired();

Primary Keys

The primary key and the associated generation strategy are as follows.

//setting a property as the key

modelBuilder.Entity<Project>().HasKey(x => x.ProjectId);

//and the generation strategy

modelBuilder.Entity<Project>().Property(x => x.ProjectId)

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

//composite keys

modelBuilder.Entity<CompositeEntity>().HasKey(x => new { x.KeyColumnAId, x.KeyColumnBId });

Navigation Properties

Navigation properties (references and collections) are as follows.

//a bidirectional many-to-one and its inverse with cascade

modelBuilder.Entity<Project>().HasRequired(x => x.Customer).WithMany(x => x.Projects).WillCascadeOnDelete(true);

//a bidirectional one-to-many

modelBuilder.Entity<Customer>().HasMany(x => x.Projects)

.WithRequired(x => x.Customer);

//a bidirectional many-to-many

modelBuilder.Entity<Technology>().HasMany(x => x.Resources)

.WithMany(x => x.Technologies);

//a bidirectional one-to-one-or-zero with cascade

modelBuilder.Entity<Project>().HasOptional(x => x.Detail)

.WithRequired(x => x.Project).WillCascadeOnDelete(true);

//a bidirectional one-to-one (both sides required) with cascade

modelBuilder.Entity<Project>().HasRequired(x => x.Detail)

.WithRequiredPrincipal(x => x.Project).WillCascadeOnDelete(true);

//a bidirectional one-to-many with a foreign key property (CustomerId)

modelBuilder.Entity<Project>().HasRequired(x => x.Customer).WithMany(x => x.Projects)

.HasForeignKey(x => x.CustomerId);

//a bidirectional one-to-many with a non-conventional foreign key column

modelBuilder.Entity<Project>().HasRequired(x => x.Customer).WithMany(x => x.Projects)

.Map(x => x.MapKey("FK_Customer_Id"));

Note: When configuring relationships, you only need to configure one endpoint.

Computed Columns

A simple column that is generated at the database by a formula, instead of being physically stored, can be done with the following.

modelBuilder.Entity<Resource>().Property(x => x.FullName)

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);

Complex Types

Declaring a class as a complex type, no need to do it for individual properties, can be done with the following.

modelBuilder.ComplexType<ContactInformation>();

//a property in a complex type

modelBuilder.ComplexType<ContactInformation>().Property(x => x.Email).IsRequired()

.HasMaxLength(50);

As you can imagine, if we use fluent mapping extensively, the OnModelCreating method can get quite complex. Realizing this, EFCF offers the possibility to group entity configurations in their own class; this class must derive from EntityTypeConfiguration<T>, and here is an example of it.

modelBuilder.Configurations.Add(new CustomerConfiguration());

public class CustomerConfiguration : EntityTypeConfiguration<Customer>

{

  public CustomerConfiguration()

  {

    this.Table("FK_Customer_Id", "dbo");

    this.Property(x => x.Name).HasMaxLength(50).IsRequired();

  }

}

Note: You are free to mix mappings in the OnModelCreating method and in configuration classes, but you should follow a consistent approach.

Identifier Strategies

Overview

Entity Framework requires that all entities have an identifier property that will map to the table’s primary key. If this primary key is composite, multiple properties can be collectively designated as the identifier.

Identity

Although Entity Framework is not tied to any specific database engine, it is certainly true that out of the box it works better with SQL Server. Specifically, it knows how to work with IDENTITY columns, arguably the most common way in the SQL Server world to generate primary keys. Until recently, it was not supported by some major database engines such as Oracle.

By convention, whenever Entity Framework encounters a primary key of an integer type (Int32 or Int64), it will assume that it is an IDENTITY column. When generating the database, it will start with value 1 and use the increase step of 1 as well. It is not possible to change these parameters.

Tip: Although similar concepts exist in other database engines, Entity Framework can only use IDENTITY with SQL Server.

Manual

In the event that the identifier value is not automatically generated by the database, it must be manually set for each entity to be saved. If it is Int32 or Int64, and you want to use attributes for the mapping, then mark the identifier property with a DatabaseGeneratedAttribute and pass it the DatabaseGeneratedOption.None. This will avoid the built-in convention that will assume IDENTITY.

[Key]

[DatabaseGenerated(DatabaseGeneratedOption.None)]

public Int32 ProjectId { get; set; }

Use the following if you prefer fluent mapping.

protected override void OnModelCreating(DbModelBuilder modelBuilder)

{

  modelBuilder.Entity<Project>().HasKey(x => x.ProjectId).Property(x => x.ProjectId)

  .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

 

  base.OnModelCreating(modelBuilder);

}

In this case, it is your responsibility to assign a valid identifier that doesn’t already exist in the database. This is quite complex, mostly because of concurrent accesses and transactions; a popular alternative consists of using a Guid for the primary key column. You still have to initialize its value yourself, but the generation algorithm assures that there won’t ever be two identical values.

public Project()

{

  //always set the id for every new instance of a Project

  this.ProjectId = Guid.NewGuid();

}

[Key]

[DatabaseGenerated(DatabaseGeneratedOption.None)]

public Guid ProjectId { get; set; }

Note: When using non-integral identifier properties, the default is to not have them generated by the database, so you can safely skip the DatabaseGeneratedAttribute.

Note: Using Guids for primary keys also has the benefit that you can merge records from different databases into the same table; the records will never have conflicting keys.

Inheritance Strategies

Consider the following class hierarchy.

An inheritance model

  1. An inheritance model

In this example, we have an abstract concept, a Tool, and three concrete representations of it: a DevelopmentTool, a TestingTool, and a ManagementTool. Each Tool must be one of these types.

In object-oriented languages, we have class inheritance, which is something relational databases don’t have. How can we store this in a relational database?

Martin Fowler, in his seminal work Patterns of Enterprise Application Architecture, described three patterns for persisting class hierarchies in relational databases:

  1. Single Table Inheritance or Table Per Class Hierarchy: a single table is used to represent the entire hierarchy; it contains columns for all mapped properties of all classes. Many of these will be NULL because they will only exist for one particular class; one discriminating column will store a value that will tell Entity Framework what class a particular record will map to.

Single Table Inheritance data model

  1. Single Table Inheritance data model
  1. Class Table Inheritance or Table Per Class: a table will be used for the columns for all mapped base class properties, and additional tables will exist for all concrete classes; the additional tables will be linked by foreign keys to the base table.

Class Table Inheritance data model

  1. Class Table Inheritance data model
  1. Concrete Table Inheritance or Table Per Concrete Class: one table for each concrete class, each with columns for all mapped properties, either specific or inherited by each class.

Concrete Table Inheritance data model

  1. Concrete Table Inheritance data model

You can see a more detailed explanation of these patterns on Martin’s web site at http://martinfowler.com/eaaCatalog/index.html. For now, I’ll leave you with some thoughts:

  • Single Table Inheritance, when it comes to querying from a base class, offers the fastest performance, because all information is contained in a single table. However, if you have lots of properties in all of the classes, it will be a difficult read, and you will have many nullable columns. In all of the concrete classes, all properties must be optional because they must allow null values. This is because different entities will be stored in the same class and not all share the same columns.
  • Class Table Inheritance offers a good balance between table tidiness and performance. When querying a base class, a LEFT JOIN will be required to join each table from derived classes to the base class table. A record will exist in the base class table and in exactly one of the derived class tables.
  • Concrete Table Inheritance for a query for a base class requires several UNIONs, one for each table of each derived class, because Entity Framework does not know beforehand in which table to look. This has the consequence that you cannot use IDENTITY as the identifier generation pattern or anyone that might generate identical values for any two tables. Entity Framework would be confused if it found two records with the same ID. Also, you will have the same columns, those from the base class, duplicated on all tables.

As for what Entity Framework is concerned, there really isn’t any difference; classes are naturally polymorphic. See Chapter 4, “Getting Data from the Database,” for learning how we can perform queries on class hierarchies.

Here’s how we can apply each of these patterns. First, here is an example for Single Table Inheritance.

public abstract class Tool

{

  public String Name { get; set; }

 

  public Int32 ToolId { get; set; }

}

public class DevelopmentTool : Tool

{

  //String is inherently nullable

  public String Language { get; set; }

}

public class ManagementTool : Tool

{

  //nullable Boolean

  public Boolean ? CompatibleWithProject { get; set; }

}

public class TestingTool : Tool

{

  //nullable Boolean

  public Boolean ? Automated { get; set; }

}

As you can see, there’s nothing special you need to do. This is the default inheritance strategy. One important thing, though: because all properties of each derived class will be stored in the same table, all of them need to be nullable. It’s easy to understand why. Each record in the table will potentially correspond to any of the derived classes, and their specific properties only have meaning to them, not to the others, so they may be undefined (NULL). In this example, I have declared all properties in the derived classes as nullables.

Let’s move on to the next pattern, Class Table Inheritance.

public abstract class Tool

{

  public String Name { get; set; }

 

  public Int32 ToolId { get; set; }

}

[Table("DevelopmentTool")]

public class DevelopmentTool : Tool

{

  public String Language { get; set; }

}

[Table("ManagementTool")]

public class ManagementTool : Tool

{

  public Boolean CompatibleWithProject { get; set; }

}

[Table("TestingTool")]

public class TestingTool : Tool

{

  public Boolean Automated { get; set; }

}

Here, the difference is that we specify a table name for all of the derived entities. There is no need to do it for the base class, we’ll just use the default. Properties of derived classes can be non-nullable, because they are stored in their own tables.

Finally, the Concrete Table Inheritance requires some more work.

public abstract class Tool

{

  protected Tool()

  {

    //create a unique id for each instance

    this.ToolId = Guid.NewGuid();

  }

  public String Name { get; set; }

 

  //Guid instead of Int32

  public Guid ToolId { get; set; }

}

[Table("DevelopmentTool")]

public class DevelopmentTool : Tool

{

  public String Language { get; set; }

}

[Table("ManagementTool")]

public class ManagementTool : Tool

{

  public Boolean CompatibleWithProject { get; set; }

}

[Table("TestingTool")]

public class TestingTool : Tool

{

  public Boolean Automated { get; set; }

}

public class ProjectsContext : DbContext

{

  protected override void OnModelCreating(DbModelBuilder modelBuilder)

  {

    //map the inherited properties for each derived class

    modelBuilder.Entity<ManagementTool>().Map(m => m.MapInheritedProperties());

    modelBuilder.Entity<TestingTool>().Map(m => m.MapInheritedProperties());

    modelBuilder.Entity<DevelopmentTool>().Map(m => m.MapInheritedProperties());

 

    base.OnModelCreating(modelBuilder);

  }

}

Some notes:

  • We cannot use the default, conventional, IDENTITY generation pattern for the primary key, because each table will have its own IDENTITY, and thus there would be records with the same primary key in each derived entity table. If we were going to query from the base class, for example, ctx.Tools.Find(1), which record would EF choose? I chose to use a Guid as the primary key, which is a common decision, hence the constructor.
  • We need to override the OnModelCreating method to complete the mapping.

Conventions

The current version of Entity Framework Code First at the time this book was written (5.0) comes along with a number of conventions. Conventions dictate how EFCF will configure some aspects of your model when they are not explicitly defined.

The full list of included conventions is comprised of the classes implementing IConvention that live in the System.Data.Entity.ModelConfiguration.Conventions namespace of the EntityFramework assembly. You can generally tell what they are supposed to do by looking at each class’ description.

The most usual conventions are:

For now, there is no way to add our own custom conventions. If we want to disable a convention, just remove its class from the DbModelBuilder instance in the OnModelCreating override.

protected override void OnModelCreating(DbModelBuilder modelBuilder)

{

  //create tables with the same names as the entities, do not pluralize them

  modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

  base.OnModelCreating(modelBuilder);

}

We have already seen how to override the conventional table and column names.

//change the physical table name

[Table("MY_PROJECT")]

public class Project

{

  //change the physical column name

  [Column("ID")]

  public Int32 ProjectId { get; set; }

}

For the connection string to use, EF lets us call a base constructor that receives a connection string.

public class ProjectsContext : DbContext

{

  //use a different connection string

  public ProjectsContext(): base("Name=SomeConnectionString") { }

}

Note: The next version of Entity Framework, 6.0, will support custom conventions.

Obtaining the Model Definition

If you need to import the model you generated using Code First to “classical” Entity Framework, it’s just a matter of exporting an EDMX file and importing it on another project.

var ctx = new  ProjectsContext(); 

XmlWriterSettings settings = new XmlWriterSettings { Indent = true };

 

using (XmlWriter writer = XmlWriter.Create("ProjectsContext.edmx", settings))  

{  

  EdmxWriter.WriteEdmx(ctx, writer);  

}  

If you open the produced file with Visual Studio, this is what you get.

The Entity Data Model definition (EDMX)

  1. : The Entity Data Model definition (EDMX)

This is a perfectly valid Entity Data Model definition, and you can import it into a project that uses “classic” Entity Framework and generate a class model from it.

Generating Code Automatically

One thing that is often requested is the ability to generate entity types that Entity Framework Code First can use straight away. This normally occurs when we have a large database with a big number of tables for which it would be hard to call classes manually. This is not exactly the purpose of Code First, but we have some options.

The first option is to start with “classic” Entity Framework. First, we add an item of type ADO.NET Entity Data Model.

Add an Entity Data Model

  1. Add an Entity Data Model

We will want to generate entities from an existing database.

Generate model from database

  1. : Generate model from database

Next, we have to create or select an existing connection string.

Set the connection string for generating the entities from the database

  1. Set the connection string for generating the entities from the database

And choose the database objects (tables, views, stored procedures) that we want to map.

Choose the database objects to map

  1. Choose the database objects to map

Finally, the model is generated.

The generated Entity Data Model

  1. The generated Entity Data Model

Tip: Inheritance is lost because EF has no way to know how we would want to have it.

As it is now, it won’t produce a Code First context and entities, but instead regular “classic” Entity Framework context and entities. In order to do the trick, we need to add a code generation item, which we can do by right-clicking the design surface and selecting that option.

Adding a DbContext generator

  1. Adding a DbContext generator

We then select the EF 5.x DbContext Generator item, and that’s it. As soon as you build your project, you will have both the context and the entities. Keep one thing in mind: the changes you make to the code will be lost if you update the model or have it regenerate the code!

Your other option is to use the Entity Framework Power Tools. This extension can be obtained from the Extension Manager of Visual Studio; at the time this book was written (August-September 2013), it is still in beta.

Entity Framework Power Tools extension

  1. : Entity Framework Power Tools extension

This extension adds a context menu to Visual Studio projects named Entity Framework. Inside of it there’s a Reverse Engineer Code First option.

Reverse engineering a Code First model

  1. : Reverse engineering a Code First model

As soon as you accept the connection string properties, Visual Studio will go to the database and will generate a context, its entities and associations for all the tables in the selected database, as well as its fluent mappings.

The generated Code First model

Figure 22: The generated Code First model

As you can see, this is way faster than with the “classic” approach. Some remarks on the process:

  • This is a one-off generation: you cannot refresh the generated entities from the database, just replace them entirely, which means you will lose any changes you made to the code.
  • The tool will generate mappings for all tables, there is no option to select just some of them.
  • Inheritances are lost, which is understandable because they don’t exist at the database level, only at the class level.

Tip: The Power Tools extension is still in beta, so you can expect some bugs to exist

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.