ADO.NET Data Services Friendly Feeds , Mapping EDM Types - I

As I mentioned in my last blog post , here are some samples of how to map your entity properties to Atom/custom markup in the atom:entry element.
You can apply Friendly Feed mappings on the EDM entity types by adding attributes to the CSDL of the Entity Data Model .
We will focus on the kinds of mappings and how to achieve them .

Pre-requisites

1. To edit the CSDL , you will need to open the EDM model in Xml view .To do this , right-click the EDM model (.EDMX file ) in Visual Studio
      and select “Open with”->”Xml Editor” .
2. Add a reference to this namespace in the <edmx:ConceptualModels> node  of the CSDL section in the EDMX file :
    ex: xmlns:m2=" https://schemas.microsoft.com/ado/2007/08/dataservices/metadata "

1) Mapping to ATOM elements in the atom:entry payload :  
The EntityPropertyMapping (EPM) attribute has two constructors , one which binds the property to an Atom element in the feed ,
and another which binds the property to a custom element . We shall discuss the former in this section.

For ATOM Mappings , the EPM markup has the following attributes .

  1. m2:FC_SourcePath: The m2:FC_SourcePath is used when
                                        a) The Property you are mapping exists in a base type and not on the current type .
                                        b) The property you are mapping exists in a complex type and not on the current type .
  2. m2:FC_TargetPath : The atom:entry element to which this property has to be mapped to
  3. m2:FC_ContentKind: The content-type of the mapped  atom:entry element
  4. m2:FC_KeepInContent : set this to false if you want the entity’s property value to turn up only in the mapped atom:entry element and not in the <contents> section.

Lets proceed  , using the same BlogPost  type that we discussed last time .
This is the EDM Markup for the type :

 <EntityType Name="BlogPost">
  <Key>
    <PropertyRef Name="BlogPostID" />
  </Key>
  <Property Name="Lat" Type="Edm.Double" Nullable="false"/>
  <Property Name="Long" Type="Edm.Double" Nullable="false"/>
  <Property Name="Published" Type="Edm.DateTime" Nullable="false" />
  <Property Name="BlogPostID" Type="Edm.Int32" Nullable="false" />
  <Property Name="Title" Type="Edm.String" Nullable="true"/>
  <Property Name="Body" Type="Edm.String" Nullable="true" />
  <Property Name="Author" Type="Edm.String" Nullable="true"/>
  <Property Name="PostURI" Type="Edm.String" Nullable="true" />
  <Property Name="ContentSummary" Type="Edm.String" Nullable="true" />
  <Property Name="IconUri" Type="Edm.String" Nullable="true"/>
</EntityType> 

1. Map the “Title” property  of the BlogPost Entity type to the entry:title element

When decorating EDM  types , the markup specifying the mapping goes on the property you are mapping ,
ex:

 <Property Name="Title" Type="Edm.String" Nullable="true"
  m2:FC_TargetPath="SyndicationTitle"
  m2:FC_EpmContentKind="Plaintext"
  m2:FC_EpmKeepInContent="true" />

2. Map the “Author” property to entry:author element 

This is what the markup would look like :

 <Property Name="Author" Type="Edm.String" Nullable="true"
           m2:FC_TargetPath="SyndicationAuthorName"
           m2:FC_ContentKind="Plaintext"
           m2:FC_KeepInContent="true" />

As described in  my previous blog post , you can map an Entity’s properties to the following atom:entry elements in the payload :

atom:entry Element m2:FC_TargetPath
entry:author/email SyndicationAuthorEmail
entry:author/name SyndicationAuthorName
entry:author/uri SyndicationAuthorUri
entry:published SyndicationPublished
entry:rights SyndicationRights
entry:summary SyndicationRights
entry:title SyndicationTitle
Content-Kind m2:FC_ContentKind
Plaintext Plaintext
Html Html
Xhtml XHtml
Default if not specified. PlainText

2) Mapping to non-ATOM/custom elements in the atom:entry payload :  

For non-ATOM/custom Mappings , the EPM markup requires the following attributes.

  1. m2:FC_SourcePath   : The property of the Entity Type whose value should be mapped
  2. m2:FC_TargetPath    : The xml path markup which describes the path to the custom markup this property should be mapped to .
  3. m2:FC_NsPrefix        : The xml prefix for the custom element/attribute that this property is mapped to.
  4. m2:FC_NsUri             : The xml namespace to which the custom element/attribute that this property is mapped should be under.
  5. m2:FC_KeepInContent : set this to false if you want the entity’s property value to turn up only in the mapped atom:entry element and not in the <contents> section.

The Xml Path syntax for custom mappings.

this syntax is very logical and looks like the following .
Lets say that you wanted to map a property to a custom element in markup that looks like this :

 <mycustomRoot xmlns=”https://www.mycustomFormat.org”>
  <customElement>property value goes here</customElement>
</mycustomRoot>

for this example ,
m2:FC_TargetPath  would be “mycustomRoot/customElement”
m2:FC_NsPrefix would be an empty string as this markup has no custom prefix.
m2:FC_NsUri would be “https://www.mycustomFormat.org
m2:FC_KeepInContent is subjective to whether you want to keep the property value in the <content> section or not.

and now , lets say that you wanted to map a property to a custom attribute of an element in markup that looks like this :

 <me:mycustomRoot xmlns:me="https://www.georss.org.georss">
  <me:customElement customAttribute="property value goes here"></me:customElement>
</me:mycustomRoot>

for this example ,
m2:FC_TargetPath  would be <“mycustomRoot/customElement/@customAttribute>”
m2:FC_NsPrefix        would be “me”.
m2:FC_NsUri  would be “https://www.mycustomFormat.org”
m2:FC_KeepContent is subjective to whether you want to keep the property value in the <content> section or not.

A note , the complexity of your custom markup has a direct effect on the performance costs  for Serialization/De-Serialization of the entity type .
with this example , lets map the lat & long properties to geoRss markup ,which looks like this :

 <geo:lat xmlns:geo="https://www.georss.org/georss">47.684</geo:lat>
<geo:long xmlns:geo="https://www.georss.org/georss">-122.122</geo:long>

Final type definition looks like this :

 <EntityType Name="BlogPost">
  <Key>
    <PropertyRef Name="BlogPostID" />
  </Key>
  <!-- map the Lat property to the <geo:lat> element  -->
  <Property Name="Lat" Type="Edm.Double" Nullable="false"
            m2:FC_TargetPath="lat"
            m2:FC_NsUri="https://www.georss.org/georss"
            m2:FC_NsPrefix="geo"
            m2:FC_KeepContent="true" />
  <!-- map the Long property to the <geo:long> element  -->
  <Property Name="Long" Type="Edm.Double" Nullable="false"
            m2:FC_TargetPath="long"
            m2:FC_NsUri="https://www.georss.org/georss"
            m2:FC_NsPrefix="geo"
            m2:FC_KeepContent="true" />
  <!-- Map the “Title” property  of the BlogPost Entity type to the entry:title element -->
  <Property Name="Title" Type="Edm.String" Nullable="true"
            m2:FC_TargetPath="EpmSyndicationTitle"
            m2:FC_ContentKind="EpmPlaintext"
            m2:FC_KeepContent="true" />
  <Property Name="Body" Type="Edm.String" Nullable="true" />
  <!-- Map the “Author” property to entry:author element  -->
  <Property Name="Author" Type="Edm.String" Nullable="true"
            m2:FC_TargetPath="EpmSyndicationAuthorName"
            m2:FC_ContentKind="EpmPlaintext"
            m2:FC_KeepContent="true" />
  <Property Name="BlogPostID" Type="Edm.Int32" Nullable="false" />
  <Property Name="Published" Type="Edm.DateTime" Nullable="false" />
  <Property Name="PostURI" Type="Edm.String" Nullable="true" />
  <Property Name="ContentSummary" Type="Edm.String" Nullable="true" />
  <Property Name="IconUri" Type="Edm.String" Nullable="true"/>
</EntityType>

Special cases

I . Complex type properties

[I’ll leave the type definitions as CLR types so that its easier to visualize the relations.]

Consider the following model ,

 public class Address
    {
        public long DoorNumber { get; set; }
        public string Street { get; set; }
        public int ZipCode { get; set; }
    }
    [DataServiceKey("CustomerID")]
    public class Customer
    {
        public int CustomerID { get; set; }
        public Address myAddress { get; set; }
    }

and lets say that you wanted to map the property Street of the complex type Address when accessed through the
entity type “Customer”  to the atom:title element ,

this can be achieved via setting the EpmSourcePath property to “Street”  .
In this case , the Epm markup would look like this :

 <EntityType Name="Customer">
  <!-- Map the Street property of the Address complex type to the entry:title element -->
  <Property Name="myAddress" Type="MyModelNamespace.Address"
            me:EpmSourcePath="Street" 
            m2:FC_Atom="true"
            m2:FC_TargetPath="EpmSyndicationTitle"
            m2:FC_ContentKind="EpmPlaintext"
            m2:FC_KeepContent="true"/>
  <!-- other properties removed for brevity-->
</EntityType>

II Mapping properties declared on base type

Consider this data model :

 [DataServiceKey("CustomerID")]
public class Customer
{
        public int CustomerID { get; set; }
        public Address myAddress { get; set; }
        public string BaseTypeField { get; set; }
}

public class DerivedCustomer : Customer
{
  public string DerivedTypeField { get; set; }
}

and you want to map a property declared on the Base type “Customer” on the derived type “DerivedCustomer”
The EPM Markup would look like this :

 <!-- Map the "BaseTypeField" property of the base type "Customer" to the entry:title element -->
<EntityType Name="DerivedCustomer" BaseType="MyModelNamespace.Customer"
            m2:FC_SourcePath="BaseTypeField" 
            m2:FC_TargetPath="EpmSyndicationAuthorName"
            m2:FC_ContentKind="EpmPlaintext"
            m2:FC_KeepContent="true">
  <!-- other properties removed for brevity-->
</EntityType>

Considerations for location of EPM markup in EDM Schema

Use the following guidelines to decide where in the EDM Schema you should add the attribute for either ATOM or custom Mappings,

Where is property defined ? Where do I apply the EPM attribute? SourcePath
Simple property on the Entity Type ex: “Title” property of “BlogPost” type above <Property> node in the <Entitytype> markup Not required
Simple property on entity’s base type <EntityType> node Base type property name
Complex Property on the Entity Type Complex Types cannot be mapped directly N/A
Simple property defined on complex type which is a property on an Entity Type <Property> node in the <EntityType> Complex type’s simple property name

Hope you enjoyed this post about applying Friendly Feeds mappings to EDMdata models.
The second part of this post will discuss more special cases in EDM Models and troubleshooting failing mappings
and also a sample project for EDM Friendly Feeds Mappings.