Share via


Working with Inheritance in Ado.net Data Services

I’ve seen this question quite often on the Astoria forums and wanted to write something down so that our users can gain from the discussions that go on in our forums.
We will discuss how one deals with entities ( tables / records / resources ) that participate in an inheritance hierarchy.
We shall take the following Data Model to discuss some key scenarios involving the Client Library.

 //Base Class
[DataServiceKey("PersonID")]
public class Person {
        public int PersonID { get; set; }
        public string Name { get; set; }
    }

//Derived Class
public class Employee : Person {
        public int EmployeeID { get; set; }
}

And you expose an IQueryable<Person> from your data source,

 public class InheritedProvider {
        static List<Person> _people;
        private static void Initialize()  {
            _people = new List<Person>(){
                new Person  {
                    PersonID =1,
                    Name="Phani"
                },
                new Employee {
                    PersonID =2,
                    EmployeeID =3,
                    Name ="Raj"
                }
            };
        }
        public IQueryable<Person> People {
            get {
                if (_people == null)
                    Initialize();
                return _people.AsQueryable<Person>();
            }
        }
    }

Browsing to the /People endpoint of the service gives you :

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

<feed …Namespace Declarations… >

  <title type="text">People</title>

  <id>https://localhost:13975/Inheritance.svc/People</id>

  <updated>2008-12-23T20:56:47Z</updated>

  <link rel="self" title="People" href="People" />

  <entry>

  <id>https://localhost:13975/Inheritance.svc/People(1)</id>

  <title type="text" />

  <updated>2008-12-23T20:56:47Z</updated>

  <author>

  <name />

  </author>

  <link rel="edit" title="Person" href="People(1)" />  

 <category term="Repro.Person" scheme="https://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> 

<content type="application/xml">

  <m:properties>

  <d:PersonID m:type="Edm.Int32">1</d:PersonID>

  <d:Name>Phani</d:Name>

  </m:properties>

  </content>

  </entry>

  <entry>

  <id>https://localhost:13975/Inheritance.svc/People(2)</id>

  <title type="text" />

  <updated>2008-12-23T20:56:47Z</updated>

  <author>

  <name />

  </author>

  <link rel="edit" title="Person" href="People(2)" /> 

 <category term="Repro.Employee" scheme="https://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

  <content type="application/xml">

  <m:properties>

  <d:PersonID m:type="Edm.Int32">2</d:PersonID>

  <d:Name>Raj</d:Name>

  <d:EmployeeID m:type="Edm.Int32">3</d:EmployeeID>

  </m:properties>

  </content>

  </entry>

  </feed>

As you can see , the <Category> element of an <entry> element represents the Entity Type that this <entry> represents.

How does this help one with working with against an Entity Set using Inheritance ?

Consider the following snippet of Client Code :

 DataServiceContext dsc = new DataServiceContext(new Uri("URI_TO_DATASERVICE"));
Employee newEmployee = new Employee() {Name = "Peter Qian"};
dsc.AddObject("People", newEmployee);
dsc.SaveChanges();

Can you guess what is wrong with this ?

Yep, since the client library sees that you are trying to insert a Person Entity , we put the Entity Type as “Person”

in the payload , i.e , the above operation results in the payload :

 <entry xmlns:d=https://schemas.microsoft.com/ado/2007/08/dataservices 
       xmlns:m=https://schemas.microsoft.com/ado/2007/08/dataservices/metadata 
       xmlns="https://www.w3.org/2005/Atom">
<updated>2008-12-23T21:23:38.5935141Z</updated>
<content type="application/xml">
   <m:properties>
     <d:EmployeeID m:type="Edm.Int32">0</d:EmployeeID>
     <d:Name>Peter Qian</d:Name>
     <d:PersonID m:type="Edm.Int32">0</d:PersonID>
   </m:properties>
</content>
</entry>

And this causes the Server to respond with :

 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="https://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-US">Error processing request stream. 
    Type information must be specified for types that take part in inheritance.</message>
</error>

This error is because , as you can see in the request payload , the <category> element which specifies the Entity Type is missing form the Request Payload.

Since the Client library doesn’t resolve the Names/Types of the Entities for you , it provides you hooks to customize the name/type resolution

so that the right entity type is specified in the payloads.

Why can’t the Client library do this automatically ?

Main reason , Since we allow POCO , the type hierarchy of entities on the client can be different compared to the server.

In your client code , set the ResolveName and ResolveType Properties on the  DataServiceContext instance to

override the default type resolution behavior.

ex :

 DataServiceContext dsc = new DataServiceContext(new Uri("URI_TO_DATASERVICE"));
dsc.ResolveName = delegate(Type entityType) {
     return entityType.FullName.Replace("<ClientNamespace>", "<ServerNamespace>");
};
dsc.ResolveType = delegate(string entitySetName) {
      return Type.GetType(entitySetName);
};
Employee newEmployee = new Employee() {Name = "Peter Qian"};
dsc.AddObject("People", newEmployee);
dsc.SaveChanges();

Since we provide the right Entity Type Name in the ResolveName property , the right Entity Type

is placed in the payload.

 <entry xmlns:d="https://schemas.microsoft.com/ado/2007/08/dataservices"            
       xmlns:m=https://schemas.microsoft.com/ado/2007/08/dataservices/metadata
       xmlns="https://www.w3.org/2005/Atom">
<category scheme="https://schemas.microsoft.com/ado/2007/08/dataservices/scheme" term="ServerNamespace.Employee" />
<updated>2008-12-23T21:52:44.8021175Z</updated>
<content type="application/xml">
    <m:properties>
      <d:EmployeeID m:type="Edm.Int32">0</d:EmployeeID>
      <d:Name>Peter Qian</d:Name>
      <d:PersonID m:type="Edm.Int32">0</d:PersonID>
    </m:properties>
  </content>
</entry>

Hope this helps .