Welcome to MSDN Blogs Sign in | Join | Help

While developing an application that spans multiple tiers , it is important that you be able to flow error information through the tiers without losing
any context or details in the tiers. With ADO.NET Data Services , we have an error contract which guarantees that all errors thrown from the Data Service ,
wrapped inside a DataServiceException , will be represented in a standard way on the wire when we send the error down to the client.

For example , consider the Query Interceptor shown below .

[QueryInterceptor("Customers")]
public Expression<Func<Customers, bool>> OnQueryCustomers()
{
        string loggedInUser = HttpContext.Current.User.Identity.Name;
        if (UserHasAccessToSet("Customers", loggedInUser))
        {
            //Filter Expression goes here
            return entity => true;
        }
        else //User does not have access to '/Customers' , throw AccessViolationException
        {
            throw new DataServiceException(403,
                "Forbidden",
                String.Format("User '{0}' cannot request data from '{1}' table", loggedInUser, "Customers"),
                "en-US");
        }
}

When the Astoria server runtime throws the above exception , the 403 value specified above  gets turned into the response status code of the request
which caused this exception.

StatusCodeInFiddler 

and the error message is serialized out to be in this format :

<?xml version="1.0" encoding="utf-8" standalone="yes" ?> 
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code>Forbidden</code> 
  <message xml:lang="en-US">User 'NoPermissions' cannot request data from 'Customers' table</message> 
</error>

At this point , the service has not lost any information that the service author intended to convey to a service consumer.

Now , lets consider the client library and how it handles this case.
Lets consider a query for the “Customers” set to which the currently logged-in user doesn't have permissions.

foreach (Customers customerEntity in northwindContext.CreateQuery<Customers>("Customers") ) {
 //Do something with the customerEntity here
}

In this case, running the above code  would result in a DataServiceQueryException being thrown.

System.Data.Services.Client.DataServiceQueryException: 
  An error occurred while processing this request. --->        
System.Data.Services.Client.DataServiceClientException: <?xml version="1.0" encoding="utf-8" standalone="yes"?> <error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> <code>Forbidden</code> <message xml:lang="en-US">User 'NoPermissions' cannot request data from 'Customers' table</message> </error>

Note that the DataServiceException thrown by the server is now set to the Message property of the InnerException of the DataServiceQueryException.
We do not de-serialize the DataServiceException into an exception type on the client side.
By default , since the exception is now a string on the client side , you don’t have direct  access to the information contained inside the Exception.
The information is all still there , just not easily accessible anymore. Since the error contract is documented and follows a standard pattern ,
we can easily write a visitor that de-serializes an exception object from the “Message” property  of the Inner Exception.

One such de-serializer for Error contracts is shown at the bottom of this post
It contains two methods :

  1. TryParse : which takes in an exception caused during Querying or updating via the client library and
    returns a DataServiceException that was thrown by the Server
  2. Throw : which takes in an exception caused during Querying or updating via the client library and
    re-throws the DataServiceException that was thrown by the Server


The new Java client joins a growing family of clients for ADO.NET Data Services, which include:

Ajax , Silverlight , .Net FX ,Dynamic Data, PHP .

Here’s some documentation about the Java client http://wiki.restlet.org/docs_2.0/13-restlet/28-restlet/287-restlet.html.

Click here to download sample application 

Using the ADO.NET Data Services Silverlight client library in x-domain and out of browser scenarios – II (Forms Authentication)
In this blog post, we will talk about using the Silverlight Client Library against a Data Service that is secured with Asp.Net Forms Authentication
In short, the whole process of authenticating against a Forms Authentication protected Data Service looks like this.
image

Server Setup

  1. Setup Forms Authentication on the Data Service Server
  2. Enable the WCF Authentication Service by following the reference here : How to: Enable the WCF Authentication Service
  3. Exclude the following resources from requiring authentication ,

3.1 The WCF Authentication Service
3.2 The ClientAccessPolicy.xml File
Ex:

<!-- The ClientAccessPolicy.xml file is required for the client to confirm if the server allows X-Domain callers.
       This should be downloadable without authenticating-->
 <location path="clientaccesspolicy.xml">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
 <!--This should be downloadable without authenticating.-->
  <location path="AuthenticationService.svc">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>

4. If the DataServiceHost does not have a global.asax file, add one to the project.
5. In the Global.asax file, we need to listen on the AuthenticationService.CreatingCookie event to customize the FormsAuth Cookie that the service creates.

Why do we need to customize the FormsAuth cookie?
The WCF Authentication service by default creates HTTPOnly Cookies.
This means that the cookie isn’t accessible by client-script.
It generally isn’t a problem when the client application is running in the same domain as the Website,
as the browser handles cookie management for us transparently.
When the client is not in the same domain as the Website, and we use the ClientHttpWebRequest networking stack,
we are unable to access any cookies marked as HttpOnly.
To work around this limitation, we recreate the FormsAuth cookie with HttpOnly set to false in the CreatingCookie event handler.
For more details: How to: Customize the Authentication Cookie from the WCF Authentication Service
Example code:

protected void Application_Start(object sender, EventArgs e)
{
//Handle the CreatingCookie event so that we can create a custom cookie with HttpOnly set to false.
//AuthenticationService.CreatingCookie on MSDN :
//http://msdn.microsoft.com/enus/library/system.web.applicationservices.authenticationservice.creatingcookie.aspx
AuthenticationService.CreatingCookie += new EventHandler<CreatingCookieEventArgs>(CreateSilverlightCompatibleHttpCookie);
}


/// <summary>
/// Creates a HttpCookie that can be read by the managed CookieContainer in ClientHttpWebRequest in Silverlight
/// </summary>
/// <param name="sender">The calling context for this event</param>
/// <param name="e">a property bag containing useful information about the HttpCookie to create</param>
void CreateSilverlightCompatibleHttpCookie(object sender, System.Web.ApplicationServices.CreatingCookieEventArgs e)
{
  int cookieVersion = 1;
  //The time at which the cookie was issued by the server
  DateTime cookieIssueDate = DateTime.Now;
  //The relative time from now when the cookie will expire and the client will have to re-authenticate.
  DateTime cookieExpiryDate = DateTime.Now.AddMinutes(30);
  //The Forms Auth ticket which uniquely identifies a user 
  //FormsAuthenticationTicket on MSDN : http://msdn.microsoft.com/en-us/library/system.web.security.formsauthenticationticket.aspx
  FormsAuthenticationTicket ticket = new FormsAuthenticationTicket
                (cookieVersion,
                 e.UserName,
                 cookieIssueDate,
                 cookieExpiryDate,
                 e.IsPersistent, /*Indicates whether the authentication cookie should be retained beyond the current session*/
                 e.CustomCredential,
                 FormsAuthentication.FormsCookiePath);
 //Creates a string containing an encrypted forms-authentication ticket suitable for use in an HTTP cookie.
 //FormsAuthentication.Encrypt on MSDN : http://msdn.microsoft.com/en-us/library/system.web.security.formsauthentication.encrypt.aspx
  string encryptedTicket = FormsAuthentication.Encrypt(ticket);
  HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
  //set HttpOnly to false so that the managed CookieContainer can read the FormsAuth cookie from the response.
  cookie.HttpOnly = false;
  cookie.Expires = cookieExpiryDate;
  HttpContext.Current.Response.Cookies.Add(cookie);
  e.CookieIsSet = true;
 }

Silverlight Client Setup

We will follow an adapter pattern which is responsible for logging in the user and injecting the FormsAuth cookie as the client library makes requests.
To start with, add a Service reference to the WCF Authentication service in the Silverlight Client application or use the one in the sample.

The FormsAuthAdapter will use the client side proxy generated for the WCF Authentication service to login the user
and hook into any attached DataServiceContext instance’s SendingRequest event to inject the FormsAuth cookie.
An instance of the FormsAuthenticationAdapter is declared at the application level.

public partial class App : Application
{
/// <summary>
/// FormsAuthenticationAdapter instance to manage authentication against a WCF Authentication Service
/// </summary>
public static FormsAuthenticationAdapter FormsAuthAdapter;


This is initialized when the Application starts.

private void Application_Startup(object sender, StartupEventArgs e)
{
string authServiceUri = String.Empty;
//extract the AuthenticationService Uri from the App.XAML file
if (this.Resources.Contains("AuthenticationServiceUri"))
{
authServiceUri = this.Resources["AuthenticationServiceUri"].ToString();
}
FormsAuthAdapter = new FormsAuthenticationAdapter(new Uri(authServiceUri, UriKind.RelativeOrAbsolute));
//The FormsAuthCookieName  should be the same value as declared in the Web.config of the server
//ex: If your web.config on the server requiring Forms Authentication is :
//<authentication mode="Forms">
//  <forms loginUrl="LoginForm.aspx" name=".ASPXFormsAUTH" protection="All" path="/" />
//</authentication>
    FormsAuthAdapter.FormsAuthCookieName = ".ASPXFormsAUTH";
    this.RootVisual = new MainPage();
    //Uncomment the below 2 lines to show the Loginwindow on application startup
    //LoginWindow login = new LoginWindow();
    //login.Show();
}

This is what our client application looks like:
image

As you can see, along with the “Install me” button, we now have a “Login” button.

When the page loads, we attach an instance of the DataServiceContext which we want to get the FormsAuth Cookie injected while
making requests to the Data Service.

//Attach the DataServiceContext instance so that we can inject the FormsAuth cookie for each request
App.FormsAuthAdapter.Attach(publicationContext);
Where the Attach Method’s signature is :
/// <summary>
/// Injects the FormsAuth cookie when the contextInstance makes a request to the DataService
/// </summary>
/// <param name="contextInstance">The DataServiceContext instance to observe</param>
public void Attach(DataServiceContext contextInstance)

Clicking the login button on the main page opens up a ChildWindow instance that we created which emulates the Login Screen.

Login button click handler:

void LoginUser(object sender, RoutedEventArgs e)
{
    LoginWindow login = new LoginWindow();
    login.Show();
    //The LoginWindow only closes if Authentication succeeds
    login.Closing += (s, eArgs) =>
    {
       /*If auth succeeds,hide the button*/
       btnLogin.Visibility = Visibility.Collapsed;
    };
  }

image
The LoginWindow’s “Login” button uses the application wide FormsAuthenticationAdapter instance, discussed above, to login the user.

private void LoginUser(object sender, RoutedEventArgs e)
{
App.FormsAuthAdapter.LoginAsync(txtUserName.Text, txtPassword.Password,
    (loginEventArgs) =>
    {
      if (loginEventArgs.Result)
       {
         /*Login succeeded*/
         this.DialogResult = true;
       }
       else
      {
         /*Login failed*/
      }
    }
    );
}
The LoginAsync method’s signature is:
  /// <summary>
/// Logs in the User and calls the LoginComplete handler
/// </summary>
/// <param name="userName">UserName to login </param>
/// <param name="passWord">password for the user account</param>
/// <param name="pLoginComplete">Called when the login process is complete</param>
public void LoginAsync(string userName, string passWord, Action<LoginCompletedEventArgs> pLoginComplete)



Once the user types in his/her username and password and hits “Login” , the Login window hits the WCF authentication service
and extracts the FormsAuth cookie from the response.When the client library makes a request to the Data Service , the FormsAuthenticationAdapter
injects the FormsAuth cookie

Common errors:

1. You receive an ArgumentException when trying to set the cookie header in the SendingRequest event.

a. System.ArgumentException occurred
  Message="The 'Cookie' header cannot be modified directly.\r\nParameter name: name"
  StackTrace:
       at System.Net.WebHeaderCollection.ThrowOnRestrictedHeader(String name, String value)
  InnerException:

Resolution: The reason you get this is because the client library is using the classic networking stack (based on XmlHttpRequest)
to make the request. In this case, the Cookie header isn’t accessible and the above exception is valid.
This is probably the only case where we would ask you to set the HttpStack property on the Client library.
To fix this:
//Set the HttpStack on the client Context instance to force the client library

//to use the ClientHttpWebRequest stack for network access
publicationContext.HttpStack = HttpStack.ClientHttp;

Additional resources:

How do I authenticate my users against the Active Directory from my Silverlight application?
In ASP.NET Forms Authentication, the Membership provider is responsible for accessing the Credential store and validating the user name and password.
By setting the Membership provider to be the ActiveDirectoryMembershipProvider , you can authenticate the user name and password the user enters
with the credentials stored in Active Directory. For more details , please refer to this MSDN article :

Using the ActiveDirectoryMembershipProvider
References :

About ClientAccessPolicy.xml files
ASP.NET Application Services.
ASP.NET Forms Authentication
Membership Providers

  1. Setup PHP on your windows machine
  2. Setup the PHP Toolkit
  3. Setting up the PHP samples on IIS
  4. Running the sample ADO.NET Data Services
  5. PHP Sample Applications Walkthrough

Setup PHP on your windows machine

The following steps show you how to setup your windows machine to host PHP applications.
We will be talking about hosting the only supported ( by MS Support ) PHP CGI application on IIS .

Using FastCGI to Host PHP Applications on IIS 7.0

  1. If you don’t find the php.ini-recommended file , then rename the php.ini-development file from your PHP folder.
  2. Once you are setup , if you get a HTTP 500 internal server error when browsing to the Php.ini file ,
    then you need setup your timezone in the PHP.ini file .
  3. To do this , find the line in the php.ini file “date.timezone” .
  4. If it has a semi-colon before it , remove it .
  5. Set the date.timezone’s  value to be a valid value from this list : List of Supported Timezones

In my php.ini , I have set the value to be date.timezone = "America/Los_Angeles"

Setup the PHP Toolkit

Follow the instructions on the codeplex site to setup your machine.
Installation and Configuration

Setting up the PHP samples on IIS

I have copied the phpsamples folder from the downloaded phpdataservices1.0 folder
into the %SystemDrive%\inetpub\wwwroot folder.This is just so that I dont need to configure any permissions
for the IIS accounts.
Open the IIS manager by running “INETMGR” from the “run” prompt on the start menu

Select the PHPSamples folder from the “Default Web Site”
Samples_Not_Application

Right-Click the Virtual directory and select “Convert to Application”
Samples_To_Application

Confirm creating the virtual directory by  pressing “OK” in the next dialog that turns up
Samples_To_Application_Confirm

This sets up the PHP applications that access the ADO.NET Data Service,
We still need to setup the actual Data Services that the PHP Samples access.

Running the sample  ADO.NET Data Services

Find the ADODotNetDataServices folder in the PHPDataServices1.0 download.
This should be under the “samples” directory.
i.e PHPDataServices1.0\samples\ADODotNetDataServices

Setup the Databases required for the project to run

1) Unzip the zipped database files from the

PHPDataServices1.0\samples\ADODotNetDataServices\data folder.
2)   Open up SQL management studio and Connect to your Database server
3) Right–click the databases node and select “Attach…”

Attach_DB_Step_1

4) In the “Attach Database” dialog , press “Add” and select the unzipped “VideoGameStore_Data.mdf” file.
You will notice that once you select the MDF file  , the dialog will complain about a missing .ldf file.
5) This is a routine warning , select the ldf file row in the dialog and press “Remove”
 Attach_DB_Step_2

For the other database Northwind, you can use the sample database that are available from Download.Microsoft.com
Here’s a link to download the Northwind sample database : Northwind and pubs Sample Databases for SQL Server 2000
Installation should be straight-forward as it comes with an MSI that is supposed to setup the database for you.

Disclaimer : I already had a copy of the Northwind database on my machine and didn’t use the installer above.
 
Once you have your databases setup , modify the respective connection strings web.config file from the ADODotNetDataServices project.

<connectionStrings>
    <add name="NorthwindEntities" 
connectionString="metadata=res://*/NorthwindModel.csdl|res://*/NorthwindModel.ssdl|res://*/NorthwindModel.msl;
provider=System.Data.SqlClient;
provider connection string=
&quot;YOURCONNECTIONSTRINGHERE&quot;"
providerName="System.Data.EntityClient" /> <add name="VideoGameStoreEntities"
connectionString="metadata=res://*/VideoGameStoreModel.csdl|res://*/VideoGameStoreModel.ssdl|res://*/VideoGameStoreModel.msl;
provider=System.Data.SqlClient;
provider connection string=
&quot;YOURCONNECTIONSTRINGHERE&quot;"
providerName="System.Data.EntityClient" /> </connectionStrings>
  1. Once you have the database and the Data Service setup, then configure the project to only run on the URL that the PHP Samples expect to find the Data Service on.
    To do this :
    In Visual Studio,Go to the properties of the Project.
  2. Click on the “Web” Tab in the left-hand pane.
  3. Under the “Servers” section , Select the “Specific Port”  option and enter “8080” for the port number.
    Setup_DataService_Select_Port
  4. Now, run the project in Visual studio and launch the PHP samples by browsing to the Index.php page
    in the PHPSamples virtual directory.

PHP Sample Applications Walkthrough

You should see this as the startup page when you run the PHP Samples
PHP_SAMPles_Screen1
Demo tab contents
PHP_SAMPles_Screen_Samples1

Select the ADO.NET Data Services Editor Sample to see some pure PHP awesomeness
PHP_SAMPles_Screen2

Here’s an editor page for the Customers Entity Set:
Customers_Edit_Screen

See that little text box over there ?
Yeah , it allows me to enter the Astoria URI operators like top , skip , filter to filter the rows bound to the grid below.
The gird is editable too , Clicking on the “Detail” link in the first column of the grid brings up an editor for the customers
entity that was selected.
Customers_Edit_Screen_2

So , there you have it , instructions to setup the samples and some tours of the sample applications.
Since I know next to nothing about PHP , I have a few questions for our PHP-Savvy users.

  1. Which application framework(s) do you use  to build PHP applications? I know about CAKE and ZEND , any others?
  2. Do you use a specific Validation framework or do you use the one that came with the  application framework?

The first in my two part blog posts about the new Silverlight Client Library we released in CTP2 is up on the team blog.

Using the ADO.NET Data Services Silverlight client in x-domain and out of browser scenarios – I

Stay tuned to the team blog for the the second  part which talks about using the client library X-Domain and Out Of Browser
to access a data service which is authenticated with Forms Authentication.

Announcement on the team blog :

http://blogs.msdn.com/astoriateam/archive/2009/08/31/ado-net-data-services-v1-5-ctp2-now-available-for-download.aspx

Download the bits here :

http://www.microsoft.com/downloads/details.aspx?FamilyID=a71060eb-454e-4475-81a6-e9552b1034fc&displaylang=en

I’ve also updated my blog posts about using the Friendly Feeds features here :

Introducing Web Friendly Feeds aka Friendly Feeds (Updated for CTP2)

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

ADO.NET Data Services Friendly Feeds , Mapping CLR Types 

Look forward to more exciting blog posts on the team blog about some of the new features in our CTP2 release .

Hello all , with the recent release of ADO.NET Data v1.5 Services CTP 1 , we introduced a new feature called as “Web Friendly Feeds”.
With the release of CTP2 , we have introduced support for 2 additional ATOM:Entry elements you can map to:

  1. entry:Summary
  2. entry:Updated

What is this feature about ?

This is what the markup for one of the the resources of Customers looks like in ADO.NET Data Services v1 .

<?xml version="1.0" encoding="utf-8" standalone="yes" ?> 
<entry 
xml:base=http://localhost:26503/northwind.svc/ 

xmlns:d
=http://schemas.microsoft.com/ado/2007/08/dataservices
xmlns:m=http://schemas.microsoft.com/ado/2007/08/dataservices/metadata
xmlns="http://www.w3.org/2005/Atom"> <id>http://localhost:26503/northwind.svc/Customers('ALFKI')</id> <title type="text" /> <updated>2009-03-18T20:30:20Z</updated> <author> <name /> </author> <category term="NorthwindModel.Customers" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> <content type="application/xml"> <m:properties> <d:CustomerID>ALFKI</d:CustomerID> <d:CompanyName>Alfreds Futterkiste</d:CompanyName> <d:ContactName>Maria Anders</d:ContactName> <d:ContactTitle>Sales Representative</d:ContactTitle> <d:Address>Obere Str. 57</d:Address> <d:City>Berlin</d:City> <d:Region m:null="true" /> <d:PostalCode>12209</d:PostalCode> <d:Country>Germany</d:Country> <d:Phone>030-0074321</d:Phone> <d:Fax>030-0076545</d:Fax> </m:properties> </content> </entry>

In the markup above , a couple of things are missing

1) The <title> element is empty

<title type="text" />

2) The author/name element is empty

<author>
    <name /> 
</author>
In v 1.5 , through Friendly Feeds , we will now be able to assign a property of the “Customers” entity type to turn up in these element locations in the atom:entry payload.

Now , let’s say that we wanted to map the “ContactName” as the author name element of the entry element and Title to be “ContactTitle” as the title of the entry element.
Then , the payload would look like this :

<entry >
  <id>http://localhost:26503/northwind.svc/Customers('ALFKI')</id> 
  <title type="text">Sales Representative</title> 
  <updated>2009-03-18T20:46:42Z</updated> 
 <author>
  <name>Maria Anders</name> 
  </author>
<category term="NorthwindModel.Customers" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
 <content type="application/xml">
 <m:properties>
  <d:CustomerID>ALFKI</d:CustomerID> 
  <d:CompanyName>Alfreds Futterkiste</d:CompanyName> 
  <d:ContactName>Maria Anders</d:ContactName> 
  <d:ContactTitle>Sales Representative</d:ContactTitle> 
  <d:Address>Obere Str. 57</d:Address> 
  <d:City>Berlin</d:City> 
  <d:Region m:null="true" /> 
  <d:PostalCode>12209</d:PostalCode> 
  <d:Country>Germany</d:Country> 
  <d:Phone>030-0074321</d:Phone> 
  <d:Fax>030-0076545</d:Fax> 
  </m:properties>
  </content>
</entry>

This also means , that IE now displays the Title & author Name elements when you browse to the “Customers” entity set .

AStoria_Feed_Reading_View

Which elements in the atom:entry element can I map to an entity type properties ?

  1. entry:author/email
  2. entry:author/name
  3. entry:author/uri
  4. entry:published
  5. entry:rights
  6. entry:summary
  7. entry:title
  8. entry:updated
  9. entry:summary

Once a property is mapped , if keeping the value of the property  in the entry:contents section doesn’t make sense,can I remove it ?
Yes , absolutely , in the above case , we can remove the ContactName & ContactTitle elements from being repeated in the <contents> section.
Ex:

<entry>
  <id>http://localhost:26503/northwind.svc/Customers('ALFKI')</id> 
  <title type="text">Sales Representative</title> 
  <updated>2009-03-18T21:04:20Z</updated> 
  <author>
  <name>Maria Anders</name> 
  </author>
  <category term="NorthwindModel.Customers" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 
 <content type="application/xml">
 <m:properties>
  <d:CustomerID>ALFKI</d:CustomerID> 
  <d:CompanyName>Alfreds Futterkiste</d:CompanyName> 
  <d:Address>Obere Str. 57</d:Address> 
  <d:City>Berlin</d:City> 
  <d:Region m:null="true" /> 
  <d:PostalCode>12209</d:PostalCode> 
  <d:Country>Germany</d:Country> 
  <d:Phone>030-0074321</d:Phone> 
  <d:Fax>030-0076545</d:Fax> 
  </m:properties>
  </content>
  </entry>

Great , what else can I do with this new feature ?
Well, did I mention you can embed your own markup in the <entry> element for an entity type !!!

consider the following entity type , BlogPost , which looks like this

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;}
}

This type represents the entry for a blog post , and also contains the Geographical location information about where the post was made ( Lat /Long)
and also the author information. Now , if this data was exposed via a Data Service, the payload would contain the Lat/Long/Published fields as part of 
the entry:content element and wouldn’t have any special meaning.
The author element should be the Author field of the atom:entry , and the Published field to be the atom:published.
the Lat & Long fields should be a georss element that shows the location at which the Blog Post was made.
In short , it should look like this :

<entry>
  <id>http://localhost/AstoriaBoard/Services/BlogSvc.svc/Posts(1)</id> 
  <title type="text">Entities, How many ways do I count thee ?</title> 
  <summary type="html">
<
img class='imgClass' width='150px' height='150px' src='/AStoriaBoard/mug.jpg'/><br/>Its a common ask that we introduce aggregatin
</summary> <published>2009-03-18T14:29:43-07:00</published> <updated>2009-03-18T21:29:43Z</updated> <author> <name>Phani Raj</name> </author> <link rel="edit" title="BlogPost" href="Posts(1)" /> <category term="Blogs.BlogPost" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> <content type="application/xml"> <m:properties> <d:BlogPostID m:type="Edm.Int32">1</d:BlogPostID> <d:Body>Its a common ask that we introduce aggregating mechanisms in Data services
so that one can do a Count of the number of entities present in an EntitySet easily.
In this blog post , I will outline one method of implementing a “Count” method that works for you.
The interface to the count method will be</d:Body> <d:Author>Phani Raj</d:Author> <d:PostURI m:null="true" /> </m:properties> </content> <geo xmlns="http://www.georss.org/georss"> <long>-80.244445</long> <lat>25.730752</lat> </geo> </entry>

Now that we got this markup , I feel like we should do something with it . How about showing this information on a MAP ?
I know !! We have a mapping solution , Virtual Earth , lets try and see if we can feed this feed to Virtual Earth and make it see our GeoRss markup.
Recently , ( November 2008 ), the Live team released the Map control as an asp.net control . Download it here :http://dev.live.com/tools/

Add this control to an aspx page in the same website as the Data Service .

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style>
        .imgClass{
            border: solid 2px #99ccff;}
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <script language="javascript">
        function pageLoad() {
            HookUpWorkAround();
        }
    </script>
    <div id="mapContainer">
        <ve:Map ID="geoRssMap" runat="server" Height="600px" Width="75%" ZoomLevel="4" />
    </div>
    <asp:ScriptManager ID="scrpManager" runat="server">
        <Scripts>
            <asp:ScriptReference Path="~/Scripts/VEWorkAround.js" />
        </Scripts>
    </asp:ScriptManager>
    </form>
</body>
</html>

In the code-behind file ,

using System;
using Microsoft.Live.ServerControls.VE;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //Clear out any mapped locations 
        geoRssMap.Clear();
        //Create a new shapelayer to import
        ShapeLayer geoRssLayer = new ShapeLayer();
        //Point the shape layer at the data source returning the mapping information
        ShapeSourceSpecification georssSpec = new ShapeSourceSpecification(
                    DataType.GeoRSS, "Services/BlogSvc.svc/Posts", geoRssLayer
                    );
        //Bind the feed to the map
        geoRssMap.ImportShapeLayerData(georssSpec, null, false);
    }
}

You might be wondering why we need the HookUpWorkAround() function and what it does , I’ll explain that in a later blog post .
After this , set the web page as the startup page and browse to the page in FireFox , I’ll explain why Firefox in a later blog post .
And voila !! This is what the page looks like :
FF_Ve_Firefox

You can download the  sample project that demonstrates this feature by clicking on the link at the bottom of this post

All this demonstrates the what and not the how , we shall discuss the how in future blog posts.

In the meantime ,enjoy this sample app and try out your own mashups.

Phastoria

More details here : Announcing the PHP Toolkit for ADO.NET Data Services

Look forward to future posts on this blog about working with the PHP library and Astoria Services.

Download sample project here :

In part 2 of this series , we will look at using the Astoria client library to create a drill down chart using the chart types available in the Silverlight toolkit.
You can take a look at the complete samples for the Silverlight toolkit here.

This sample builds a UI that looks like this :

ChartDrillDownAstoria_2

The Pie chart represents the distribution of Employees across departments.
Clicking on a specific piece of the pie  brings up the list of the employees in that department.

Binding the Pie chart
The Chart control has a Pie series that has the DependentValuePath set to the Count of employees in the department
and the IndependentValuePath set to the name of the department.

Setting the IndependentValuePath is straightforward ,set the IndependentValuePath to the “DepartMentName” property of the Department entity type. The DependentValuePath is a little tricky , as the Department entity doesnt contain aggregation information regarding the employees in the Department . But , it does have the employees as an ObservableCollection of Employee types . So , we can get the count by assigning the DependentValuePath to be the expression “Employees.Count” which gets the Count property of the IList. Unfortunately ,  this means that the Employees will also have to be downloaded when you bind the Departments. In another post , I will discuss how to lazy load the employees list and still get this aggregation.

XAML for Pie Series in Chart

  <chartingToolkit:Chart x:Name="chEmployeesByDepartment" 
Title="# Employees/Department" IsEnabled="true" Height="300"> <chartingToolkit:Chart.Series> <chartingToolkit:PieSeries
x:Name="lnSeries" IndependentValuePath ="DepartMentName" DependentValuePath ="Employees.Count" SelectionChanged="DepartmentSelected" IsSelectionEnabled ="True" > </chartingToolkit:PieSeries> </chartingToolkit:Chart.Series> </chartingToolkit:Chart>


The function to bind the Pie series with the Department information

 private void LoadDepartmentsAndEmployees() {
    DataServiceQuery<Department> deptQueryWithEmployees = context.Departments.Expand("Employees") 
as DataServiceQuery<Department>; deptQueryWithEmployees.QueryAndCall( (deparmentStatistics) => { Dispatcher.BeginInvoke( () => { PieSeries series = chEmployeesByDepartment.Series[0] as PieSeries; series.ItemsSource = deparmentStatistics; } ); }); }

Populating employee information when a department is selected in the Pie Series
We need to setup the Pie Series in the chart control to allow selection of chart points  and hook into the
SelectionChanged event of the Pie Series. We do this by:

  1. Setting IsSelectionEnabled to true on the Pie Series
  2. Hooking up to the SelectionChanged event of the Pie Series
        private void DepartmentSelected(object sender, SelectionChangedEventArgs e) {  
            //Get the series that caused this  event to be raised
            PieSeries series = sender as PieSeries;
            //Get the current selected department in the Series
            Department selectedDepartment = series.SelectedItem as Department;
            if (selectedDepartment != null) { 
                //Set the title of the data grid
                lblSelectedDepartment.Text = String.Format(strSelectedDepartmentMessage, selectedDepartment.DepartMentName);
                //Bind the employee grid with the employees for this department
                dgEmployees.ItemsSource = selectedDepartment.Employees;
            }  
        }      

Download sample project here :

Download the sample Silverlight project here :

About the Silverlight toolkit  ,

From their Codeplex page : http://silverlight.codeplex.com/

“The Silverlight Toolkit is a collection of Silverlight controls, components and utilities made available outside the normal Silverlight release cycle”

In the first of a series I am planning , we will talk about how to achieve a Master-Child display using the Accordion control 
from the Silverlight Toolkit control.

The data model we will be binding looks like this :

SLToolkitAccordion_DataModel

Type Employee has a property called Department of type Department.
Type Department has a collection of type Employee called Employees.

Employee and Department are related 1..1
Department and Employee are related 1..M

We want to bind the Department names to the header of the Accordion and
the employees as a list inside the content of the accordion.
When we are done , the final output should look like this :

SLTOolkit_Accordionoutput

Configuring the header to show the DepartmentName

The Header template of the Accordion has a textblock which binds to the DepartMentName property of the
Department entity.

 <layoutToolkit:Accordion.HeaderTemplate >
       <DataTemplate>
            <TextBlock Text="{Binding Path=DepartMentName}"></TextBlock>
       </DataTemplate>
 </layoutToolkit:Accordion.HeaderTemplate>

Configuring the content to show the names of the employees working in the department.

The content of the Accordion pane would be a list box which is bound to the Employees collection of the Department
entity type and shows the EmployeeName as the DisplayMember.

<layoutToolkit:Accordion.ContentTemplate>
      <DataTemplate>
          <ListBox ItemsSource="{Binding Employees}" DisplayMemberPath="EmployeeName">
          </ListBox>
      </DataTemplate>
</layoutToolkit:Accordion.ContentTemplate>

To write the Silverlight client code for this project ,we will need to generate the Client classes with Databinding enabled.
As shown in this article on our team blog .

Once this is done , we have two ways of binding the accordion ,
Eager Loading : Download the employees for a department when you download the departments
Lazy Load : Download the employees for a department when the header of an accordion pane , i.e Department is clicked.

Eager Loading :

DataServiceQuery<Department> deptQueryWithEmployees = context.Departments.Expand("Employees") as DataServiceQuery<Department>;
deptQueryWithEmployees.QueryAndBind(acDepartments);

Since the query part itself isnt interesting in the context of this blog post , I’ve abstracted away the querying into an extension method ( QueryAndBind ) and will be available as part of the download.

Here , as you can see , we are eager loading the Employees for the departments .
I feel that this is wasteful if you have a large number of departments as this not only increases the amount of data on the wire , but it also means that all the data that comes down the wire will be useful .

For example, if you have about 15 departments and a user may click on 5 or less departments , then downloading employees for all the 15 departments seems inefficient.

Xaml for Eager Loading

<layoutToolkit:Accordion x:Name="acDepartments" Width="400">
    <layoutToolkit:Accordion.HeaderTemplate >
        <DataTemplate>
            <TextBlock Text="{Binding Path=DepartMentName}"></TextBlock>
        </DataTemplate>
    </layoutToolkit:Accordion.HeaderTemplate>
    <layoutToolkit:Accordion.ContentTemplate>
        <DataTemplate>
            <ListBox ItemsSource="{Binding Employees}" DisplayMemberPath="EmployeeName">
            </ListBox>
        </DataTemplate>
    </layoutToolkit:Accordion.ContentTemplate>
</layoutToolkit:Accordion>

Lazy Loading :
In this case , we will only bind the Headers and  will bind the Employees only if the header is clicked for that department. In the Silverlight Accordion control , the event SelectionChanged is fired when the header of an Accordion Pane is clicked.
By listening to this event , we can find out which department was clicked and load the Employees for that department using BeginLoadProperty .

public DelayLoad() {
           InitializeComponent();
           context = new TreeViewDataProvider(new Uri("TreeViewDataService.svc", UriKind.RelativeOrAbsolute));
           LoadDepartments();
}
private void LoadDepartments(){
    ((DataServiceQuery<Department>)context.Departments).QueryAndBind(acDepartments);
}
We will subscribe to the SelectionChanged event of the Accordion and add “LoadEmployeesForDepartment” as the event handler.
private void LoadEmployeesForDepartment(object sender, SelectionChangedEventArgs e)
        {
            //The sender is the control that raised the event
            Accordion acControl = sender as Accordion;
            //Get the Selected Department
            Department selectedDepartment = acControl.SelectedItem as Department;
            if (
                //If the Selected object is department
                selectedDepartment != null 
                && (
                // If the Employees collection is null or empty
                selectedDepartment.Employees == null || selectedDepartment.Employees.Count == 0)
                )
            {
                //Call load property , which updates the Employees collection of this instance 
                //and the UI automatically updates itself, since Department type implements INotifyPropertyChanged
                context.LoadPropertyAndCall<Employee>(selectedDepartment, "Employees",
                    null);
            }

        }

XAML for Delay Load :

<layoutToolkit:Accordion x:Name="acDepartments" SelectionChanged="LoadEmployeesForDepartment">
    <layoutToolkit:Accordion.HeaderTemplate >
        <DataTemplate>
            <TextBlock Text="{Binding Path=DepartMentName}"></TextBlock>
        </DataTemplate>
    </layoutToolkit:Accordion.HeaderTemplate>
    <layoutToolkit:Accordion.ContentTemplate>
        <DataTemplate>
            <ListBox ItemsSource="{Binding Employees}" DisplayMemberPath="EmployeeName">
            </ListBox>
        </DataTemplate>
    </layoutToolkit:Accordion.ContentTemplate>
</layoutToolkit:Accordion>

Download the sample Silverlight project here :

Running the sample app

  1. Set SLToolkitWithAstoriaWeb as the Start-up project.
  2. Set SLToolkitWithAstoriaTestPage.html as the start-up page.
  3. To run the EagerLoading sample , just hit F5
  4. To run the DelayLoad sample , follow a-c and add the Query string ?delayLoad to the address bar in the browser that comes up.

As an extension to the last blog post dealing with Set based filter operations in our client library ,
we will introduce support for the specifying method calls in the filter expression.

What does this achieve ?
Currently , the IsIn operator only supports an equality comparision.
With support for Method Calls , you can now select entities in a set which when passed to a method , evaluate true. ex: You can generate Uris such as this :
  1. /northwind.svc/Customers?$filter = substringof('London',City) or substringof('Berlin',City) or substringof('Prague',City)
  2. /northwind.svc/Customers?$filter = startswith('London',City) or startswith('Berlin',City) or startswith('Prague',City)
  3. /northwind.svc/Customers?$filter = endswith('London',City) or endswith('Berlin',City) or endswith('Prague',City)

Fortunately , not a lot of code change is required to get this support.

We will change the first parameter of the extension method from

Expression<Func<TEntity, object>> propertyExpression

to

Expression<Func<TEntity, TValue, bool>> comparisionInvokeExpression

which means that where we were initially sending an expression that selects a property of the entity , we now send the Extension method a delegate that accepts the entity and the value being compared against it and returns a boolean value after comparison using a method.

example :

Expression<Func<T, object>> propertyExpression means
customer => customer.City
Expression<Func<TEntity, TValue, bool>> comparisionInvokeExpression means
(cust, cityName) => cust.City.ToLower().StartsWith(cityName)

The second change is in the location where we build the comparision expression for values in the set.
we change the line which does the comparision using Expression.Equal with a method call to the comparision expression passed in .
We will change  :

//Build a comparision expression which equats the Id of the ENtity with this value in the IDs list
// ex : e.Id == 1
Expression comparison = Expression.Equal(left, Expression.Constant(id));
to :
//The Left Hand Side of the Filter Expression
MethodCallExpression comaprisionMethod = comparisionInvokeExpression.Body as MethodCallExpression;
//Build a comparision expression which calls the method that does the comparision for us
//ex : c=> c.City.Contains(id)
Expression comparison = Expression.Call(
                        comaprisionMethod.Object,
                        comaprisionMethod.Method,
                        Expression.Constant(id) );

The complete code sample is here :

I'll be joining Shayne Burgess and Mike Flasko at Tech Ed in LA .

If you have question/feedback about project Astoria , please drop by the Data Development Technical Learning Center booth at Tech Ed.

Here's the times at which I'll be available at the TLC booth.

 

Tuesday, May 12, 2009

Area 

Station 

9:30 AM - 12:30 PM 

Developer Tools, Languages and Frameworks

TLC/BLUE/DTL: Microsoft Data Development 

Wednesday, May 13, 2009

Area 

Station 

12:15 PM - 3:15 PM 

Developer Tools, Languages and Frameworks 

TLC/BLUE/DTL: Microsoft Data Development 

Thursday, May 14, 2009

Area 

Station 

8:00 AM - 11:00 AM

Developer Tools, Languages and Frameworks 

TLC/BLUE/DTL: Microsoft Data Development 

 

Telerik has a range of controls that work with ADO.NET Data Services as the data source . Take a look at them here :

ADO.NET Data Services with Telerik Controls

Kevin Babcock has a great post over at Telerik where he talks about using ADO.NET Data Services with Telerik Reporting suite .

I wanted to address one small point in the blog post which I felt can be improved upon.

“You might be curious why I chose to iterate through the list of categories in the report parameter value, calling the web service for each one and appending the results to a collection.
The reason is that, due to the limitations of ADO.NET Data Services, you can’t use methods like Contains to filter data in your LINQ queries.“

Yes , this is absolutely correct.
We don't support the “Contains” operator to select a primitive property of the Entity Type from a given range of values.
The client linq implementation doesn't support Contains on Navigation properties is because we don't have a URI Query operator  that corresponds to the "Contains" function to select a value from a set. But you can semantically achieve the same effect by “OR”ing a couple of “EQUALS” expressions.

ex:

If A={0,1,2,3}  , then A.Contains(B) is equivalent to ( B == A[0] OR B ==A[1] OR B == A[2])

I wrote about achieving this effect here :  Set Based Operations in Ado.net Data Services

Now, in this sample , we are filtering an Entity Set (Products) based on the value of a primitive property ( CategoryID ) of a Navigation Property (Categories).
Now , since this is the Northwind model Schema, Products are related to categories in a 1..M association and Categories to Products in a 1..M association.

What this means is that the above query can be expressed as : 

Now , this is still not as optimal as it can be , but we reduced the number of round trips as we download only relevant categories and their associated Products.
To make this even easier , we use the IsIn<T> extension method I wrote from Set Based Operations in Ado.net Data Services.

So , there you have it , we reduced the number of network calls and also the lines of code to achieve this filtering.

On a side note , Kevin has left Telerik and now blogs at : http://www.myviewstate.net/blog/

All the best for your future endeavours Kevin !!

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="http://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=”http://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 “http://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="http://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 “http://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="http://www.georss.org/georss">47.684</geo:lat>
<geo:long xmlns:geo="http://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="http://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="http://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.

As part  of the v1.5 install, we also shipped a Silverlight 2.0 SDK library that is capable of talking to a v1.5 Astoria Service.

Did you know that you could use the Silverlight 2.0 client library we shipped as part of our v1.5 CTP1 release in Silverlight 3.0 applications?

From a Silverlight SDK library point of view ,to use Mike Flasko’s words, ”we are nothing but user code”.
We have no dependencies on the Runtime that have changed in SL 3.0 , yet.
So , you can use the Silverlight 2.0 SDK library that we shipped in v1.5 CTP1 , by
replacing the reference to “System.Data.Services.Client.dll” with “Microsoft.Data.Services.Client.dll”

In your Silverlight project ,

1. If you already have a reference to “System.Data.Services.Client.dll” in your SIlverlight application , remove it .

2. If you have the v1.5 CTP1 installed , the Silverlight client binaries are stored in the following location ,
%ProgramFiles%\ADO.NET Data Services V1.5 CTP1\sl_bin

3. Bring up the “Add Reference” window by right clicking the “References” node for the Silverlight 3.0 Project in Visual Studio.

4. Use the “Browse” tab of the “Add Reference” window to browse to this location to add a reference to “Microsoft.Data.Services.Client.dll”.

5. Rebuild and run the app .

What about Code-gen ,
Can I point the Silverlight 3.0 application at a v1.5 CTP1 service and get all the binding & Friendly Feeds mappings generated in the Client types?

No , there is no integration for v1.5 codegeneration tools in Visual Studio for Sl 3.0 Projects .
You will have to use the DataSvcUtil.exe tool from the following directory manually to generate the types with binding information for SL 3.0 projects .

Location for DataSvcUtil.exe : %ProgramFiles%\ADO.NET Data Services V1.5 CTP1\bin

From a runtime perspective , is there any functionality in the v1.5 SL 2.0 library that is not present when the dll is inside a xap built with SL 3.0 tools ?

No , none , everything should just work . If something doesn’t work , please leave a comment on this post or write to me at PhaniRaj At Microsoft Dot Com.

What about Silverlight 3 Out-Of-Browser applications ?

The Astoria Silverlight client library doesn’t yet work with Out of the Browser Silverlight applications.

More Posts Next page »
 
Page view tracker