Ju-Yi Kuo's blog

EF Mapping Advisor (1) : The Language of Mapping

In this post I will start explaining what’s behind the scene of ADO.NET Entity Framework mapping layer. Specifically I will focus on the direct mapping between entity sets and store tables. Other kinds of mapping such as stored procedure mapping will come later in this series.

When it comes to mapping specification, ADO.NET Entity Framework takes a unique approach. You specify mapping by specifying a set of query pairs, where each query pair corresponds to a mapping fragment.

To understand what I mean, let’s look at an example. Suppose we have a Person entity type and a Customer entity type which derives from the Person entity type. I want to split the Person entity type horizontally and map it to 2 tables in the store: PersonTable and AddressTable. This is a mapping strategy we called entity splitting. Furthermore the Customer type will have its own unique members mapped to yet another table, the CustomerTable. This is the typical table-per-type (TPT) mapping strategy to map an inheritance hierarchy. You can find the three artifact files below.

CSDL file:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<Schema Namespace="EDMExample" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">

  <EntityContainer Name="EDMExampleContainer">

    <EntitySet Name="PersonSet" EntityType="Self.Person" />

  </EntityContainer>

  <EntityType Name="Person">

    <Key>

      <PropertyRef Name="Id" />

    </Key>

    <Property Name="Id" Nullable="false" Type="Int32" />

    <Property Name="Name" Type="String" MaxLength="512" FixedLength="false" Unicode="false" />

    <Property Name="Address" Type="String" MaxLength="512" FixedLength="false" Unicode="false" />

  </EntityType>

  <EntityType Name="Customer" BaseType="EDMExample.Person">

    <Property Name="CreditScore" Type="Int32" />

  </EntityType>

</Schema>

SSDL file:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<Schema Namespace="EDMExample.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2005" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">

  <EntityContainer Name="EDMExampleStoreContainer">

    <EntitySet Name="PersonTable" EntityType="EDMExample.Store.PersonTable" Schema="dbo" Table="PersonTable" />

    <EntitySet Name="AddressTable" EntityType="EDMExample.Store.AddressTable" Schema="dbo" Table="AddressTable" />

    <EntitySet Name="CustomerTable" EntityType="EDMExample.Store.CustomerTable" Schema="dbo" Table="CustomerTable" />

    <AssociationSet Name="FK_Address_PersonSet" Association="EDMExample.Store.FK_Address_Person">

      <End Role="Person" EntitySet="PersonTable" />

      <End Role="Address" EntitySet="AddressTable" />

    </AssociationSet>

    <AssociationSet Name="FK_Customer_PersonSet" Association="EDMExample.Store.FK_Customer_Person">

      <End Role="Person" EntitySet="PersonTable" />

      <End Role="Customer" EntitySet="CustomerTable" />

    </AssociationSet>

  </EntityContainer>

  <EntityType Name="PersonTable">

    <Key>

      <PropertyRef Name="Id" />

    </Key>

    <Property Name="Id" Type="int" Nullable="false" />

    <Property Name="Name" Type="varchar" Nullable="true" MaxLength="512" />

  </EntityType>

  <EntityType Name="AddressTable">

    <Key>

      <PropertyRef Name="Id" />

    </Key>

    <Property Name="Id" Type="int" Nullable="false" />

    <Property Name="Address" Type="varchar" Nullable="true" MaxLength="512" />

  </EntityType>

  <EntityType Name="CustomerTable">

    <Key>

      <PropertyRef Name="Id" />

    </Key>

    <Property Name="Id" Type="int" Nullable="false" />

    <Property Name="CreditScore" Type="int" Nullable="true" />

  </EntityType>

  <Association Name="FK_Address_Person">

    <End Role="Person" Type="EDMExample.Store.PersonTable" Multiplicity="1" />

    <End Role="Address" Type="EDMExample.Store.AddressTable" Multiplicity="0..1" />

    <ReferentialConstraint>

      <Principal Role="Person">

        <PropertyRef Name="Id" />

      </Principal>

      <Dependent Role="Address">

        <PropertyRef Name="Id" />

      </Dependent>

    </ReferentialConstraint>

  </Association>

  <Association Name="FK_Customer_Person">

    <End Role="Person" Type="EDMExample.Store.PersonTable" Multiplicity="1" />

    <End Role="Customer" Type="EDMExample.Store.CustomerTable" Multiplicity="0..1" />

    <ReferentialConstraint>

      <Principal Role="Person">

        <PropertyRef Name="Id" />

      </Principal>

      <Dependent Role="Customer">

        <PropertyRef Name="Id" />

      </Dependent>

    </ReferentialConstraint>

  </Association>

</Schema>

MSL file:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<Mapping Space="C-S" xmlns:cdm="urn:schemas-microsoft-com:windows:storage:mapping:CS" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">

  <EntityContainerMapping StorageEntityContainer="EDMExampleStoreContainer" CdmEntityContainer="EDMExampleContainer">

    <EntitySetMapping Name="PersonSet">

      <EntityTypeMapping TypeName="IsTypeOf(EDMExample.Person)">

        <MappingFragment StoreEntitySet="PersonTable">

          <ScalarProperty ColumnName="Id" Name="Id" />

          <ScalarProperty Name="Name" ColumnName="Name" />

        </MappingFragment>

 

        <MappingFragment StoreEntitySet="AddressTable">

          <ScalarProperty ColumnName="Id" Name="Id" />

          <ScalarProperty Name="Address" ColumnName="Address" />

        </MappingFragment>

      </EntityTypeMapping>

      <EntityTypeMapping TypeName="EDMExample.Customer">

        <MappingFragment StoreEntitySet="CustomerTable">

          <ScalarProperty ColumnName="Id" Name="Id" />

          <ScalarProperty Name="CreditScore" ColumnName="CreditScore" />

        </MappingFragment>

      </EntityTypeMapping>

    </EntitySetMapping>

  </EntityContainerMapping>

</Mapping>

Both the CSDL and SSDL files are easily understood. I want to focus on the MSL file and explain how EF runtime reasons about the content of this file. As you can see, I have highlighted 3 mapping fragments. So what do these fragments mean? Well, when EF runtime looks at a mapping fragment, it considers it to be a pair of equivalent Entity SQL queries (Entity SQL is an extension of the SQL language that is supported by the Entity Framework):

                QC = QS

The query on the left hand side is an ESQL query against a Conceptual side entity type which returns rows of scalar values; the query on the right is an ESQL query on a Store side table which returns rows of scalar values, and these 2 result sets should be equal. If they are not, then it’s a mapping error and EF will throw an exception. You can also think of QC as a view V on the entity, and it should be equal to the view that QS represents. This means QC = V = QS. From now on we will call these queries “fragment queries”.

As an example, let’s look at the first highlighted fragment. It is under the IsTypeOf(Person) type mapping element, therefore EF will consider the C side query to be:

                QC = SELECT c.Id, c.Name FROM PersonSet c

Notice that this query will return results from both Person and Customer instances. Since the mapping fragment maps to PersonTable, EF considers the S side query to be:

                QS = SELECT Id, Name FROM PersonTable

The results from these 2 queries should be equivalent. This, is the EF language of mapping. You can see that the Person entity type has 2 mapping fragment related to it, and the Customer entity type has 3 mapping fragments related to it. So in a sense, the Person entity instances are horizontally split into 2 portions and the Customer entity instances are horizontally split into 3 portions as indicated in the picture below.

 

 

EF will “stitch up”, or join these queries to materialize Person and Customer instances. But how, you may ask? I will provide some more details in my next post.

 

 

Published Wednesday, May 21, 2008 7:44 AM by juyik
Attachment(s): blog1fig1.jpg

Comments

 

Jaroslaw Kowalski said:

Ju-Yi Kuo from our team has started blogging about advanced mapping scenarios supported by Entity Framework.

June 5, 2008 4:19 PM
 

Hot Topics said:

Ju-Yi Kuo , from the Entity Framework team, has started a new blog with posts on mapping in the EDM with

June 13, 2008 8:16 AM
Anonymous comments are disabled

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker