ADO.NET Data Services Friendly Feeds , Mapping CLR Types
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 CLR entity types by decorating the Entity classes with the EntityPropertyMappingAttribute type.
We will focus on the kinds of mappings and how to achieve them .
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 Attribute constructor takes the following parameters.
- propertyName : The property of the Entity Type whose value should be mapped
- targetSyndicationItem : The atom:entry element to which this property has to be mapped to
- targetTextContentKind : The content-type of the mapped atom:entry element
- 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 .
public class BlogPost
{
public double Lat { get; set; }
public double Long { get; set; }
public int BlogPostID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string Author { get; set; }
public string PostURI { get; set; }
public string ContentSummary { get; set; }
}
1. Map the “Title” property of the BlogPost Entity type to the entry:title element
2. Map the “Author” property to entry:author element
This is what the code would look like :
[EntityPropertyMapping(
"Title",/*Source property path , the property of the Entity type to be mapped*/
SyndicationItemProperty.Title,/* Syndication item to which the Source Property is mapped*/
SyndicationTextContentKind.Plaintext,/* Syndication content kind for the syndication item this property is bound to */
true/* If false the property value is only placed at the mapped location & removed from the <content> section of the atom:entry*/
)]
[EntityPropertyMapping("Author", SyndicationItemProperty.AuthorName, SyndicationTextContentKind.Plaintext, true)]
public class BlogPost
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 | TargetSyndicationItem |
entry:author/email | SyndicationItemProperty.AuthorEmail |
entry:author/name | SyndicationItemProperty.AuthorName |
entry:author/uri | SyndicationItemProperty.AuthorUri |
entry:published | SyndicationItemProperty.Published |
entry:rights | SyndicationItemProperty.Rights |
entry:summary | SyndicationItemProperty.Summary |
entry:title | SyndicationItemProperty.Title |
entry:Updated | SyndicationItemProperty.Updated |
entry:contributor/name | SyndicationItemProperty.ContributorName |
entry:contributor/email | SyndicationItemProperty.ContributorEmail |
entry:contributor/uri | SyndicationItemProperty.ContributorUri |
2) Mapping to non-ATOM/custom elements in the atom:entry payload :
For non-ATOM/custom Mappings , the EPM Attribute constructor takes the following parameters.
- propertyName : The property of the Entity Type whose value should be mapped
- targetName (Target Path) : The xml path markup which describes the path to the custom markup this property should be mapped to .
- targetNamespacePrefix: The xml prefix for the custom element/attribute that this property is mapped to.
- targetNamespaceUri: The xml namespace to which the custom element/attribute that this property is mapped should be under.
- 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 ,
targetName would be “mycustomRoot/customElement”
targetNamespacePrefix would be an empty string as this markup has no custom prefix.
targetNamespaceUri would be “https://www.mycustomFormat.org”
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 :
<mycustomRoot xmlns:me="https://www.georss.org.georss">
<me:customElement customAttribute="property value goes here"></me:customElement>
</mycustomRoot>
for this example ,
targetName would be <“mycustomRoot/customElement/@customAttribute>”
targetNamespacePrefix would be “me”.
targetNamespaceUri would be “https://www.mycustomFormat.org”
keepInContent 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 :
[EntityPropertyMapping(
"Title",/*Source property path , the property of the Entity type to be mapped*/
SyndicationItemProperty.Title,/* Syndication item to which the Source Property is mapped*/
SyndicationTextContentKind.Plaintext,/* Syndication content kind for the syndication item this property is bound to */
true/* If false the property value is only placed at the mapped location & removed from the <content> section of the atom:entry*/
)]
[EntityPropertyMapping("Author", SyndicationItemProperty.AuthorName, SyndicationTextContentKind.Plaintext, true)]
[EntityPropertyMapping("Lat",/*Source property path , the property of the Entity type to be mapped*/
"lat",/*The xml path markup which describes the path to the custom markup this property should be mapped to . */
"geo",/*The xml prefix for the custom element/attribute that this property is mapped to. */
"https://www.georss.org/georss", /*The xml namespace to which the custom element/attribute that this property is mapped should be under*/
true /*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. */
)]
[EntityPropertyMapping("Long", "long", "geo", "https://www.georss.org/georss", true)]
public class BlogPost
{
public double Lat { get; set; }
public double Long { get; set; }
public DateTime Published { get; set; }
public int BlogPostID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string Author { get; set; }
public string PostURI { get; set; }
public string ContentSummary{ get;set; }
public string IconUri {get;set;}
}
Special cases
I . Complex type properties
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 propertyName property to an appropriate path .
because propertyName not just takes the property name , it also accepts a path to the property’s location.
in this case , the propertyName would be : “myAddress/Street” , and the Epm attribute would look like this :
[DataServiceKey("CustomerID")]
[EntityPropertyMapping(
"myAddress/Street",
SyndicationItemProperty.Title,
SyndicationTextContentKind.Plaintext,
true
)]
public class Customer
{
public int CustomerID { get; set; }
public Address myAddress { get; set; }
}
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 type declaration would look like this :
[EntityPropertyMapping("BaseTypeField", SyndicationItemProperty.Title, SyndicationTextContentKind.Plaintext, true)]
public class DerivedCustomer : Customer {
public string DerivedTypeField { get; set; }
}
In other words , when specifying the propertyName , property paths always make simple properties look like they are declared
on the type which has annotations , mapping inherited properties is no different from mapping properties declared on the same type.
Considerations for location of EPM Attribute
Use the following guidelines to decide where in the entity model you should add the attribute for either ATOM or custom Mappings,
Where is property defined ? | Where do I apply the EPM attribute? |
Simple property on the Entity Type ex: “Title” property of “BlogPost” type above | Entity Type |
Simple property on entity’s base type | Derived Entity Type or Base Entity Type |
Complex Property on the Entity Type | Complex Types cannot be mapped directly See , 3 |
Simple property defined on complex type which is a property on an Entity Type | Entity Type |
Hope you enjoyed this post about applying Friendly Feeds mappings to CLR data models.
The next blog post will discuss how to apply the same mappings to EDM models.
Comments
Anonymous
March 20, 2009
Thank you for submitting this cool story - Trackback from DotNetShoutoutAnonymous
March 21, 2009
Phani, a data services test team member, has posted two nice write ups describing the "web friendlyAnonymous
March 23, 2009
Thank you for this interesting article. Phani, do you know how to custimize feeds using Entity Framework?Anonymous
March 24, 2009
Hi Constantine, I will write about customizing feeds with the Entity Framework sometime over this week. Stay tuned. -PhaniAnonymous
March 29, 2009
Phani, a member of the data services team, has continued his great series on Web Friendly Feeds withAnonymous
July 10, 2009
Is there a way to force ADO.NET services to ignore a property? We need a solution for that, so that we can extend the ADO.Services partial clases with an extra set of properties. The new extended properties don't have any relationship with the data model, they can be state properties. Thanks.Anonymous
September 05, 2009
Why is this a class attribute rather than a property attribute? It makes harder to read, and to refactor property name.Anonymous
September 08, 2009
Hi Kojishi, Thanks for the feedback We placed the property on the Class as we wanted to support the case of mapping a base type's properties in the derived type. If we put the attribute on the property , then we wouldn't be able to support this scenario.