title: Inheritance is not supported, even with a custom OData Service Provider?
body: <p><strong>EDIT</strong>: I went ahead and created a little project to demonstrate this issue (called RestfulTimesTest), available on <a href="https://skydrive.live.com/#cid=0E945D83357E9465&amp;id=E945D83357E9465!108&amp;sc=documents" rel="nofollow">SkyDrive</a>.</p>
  
  <p>I built a custom <code>OData</code> Service Provider to provide queries and updates into a custom model, based on <code>Alex James'</code> excellent blog post on <a href="http://blogs.msdn.com/b/alexj/archive/2010/01/04/creating-a-data-service-provider-part-1-intro.aspx" rel="nofollow">Creating a Data Service Provider</a>.</p>
  
  <p>Consider the following 3 CLR classes: <code>ResidentialCustomer</code>, <code>Customer</code>, and <code>User</code>.  <code>ResidentialCustomer</code> extends <code>Customer</code>, <code>Customer</code> has a list of <code>User</code>s, and <code>User</code> has a reference back to <code>Customer</code>.</p>
  
  <p>The issue I'm having is that the metadata can include <code>ResidentialCustomer</code> or the association between <code>Customer</code> and <code>User</code>, but not both.  If I include both, I get the following error when trying to display or access the metadata via the <code>DataService</code>:</p>
  
  <pre><code>{System.NullReferenceException: Object reference not set to an instance of an object.
  at System.Data.Services.Providers.DataServiceProviderWrapper.GetResourceAssociationSet(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
  at System.Data.Services.Serializers.MetadataSerializer.MetadataManager.GetAndValidateResourceAssociationSet(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty navigationProperty)
  at System.Data.Services.Serializers.MetadataSerializer.MetadataManager.PopulateAssociationsForSetAndType(ResourceSetWrapper resourceSet, ResourceType resourceType)
  at System.Data.Services.Serializers.MetadataSerializer.MetadataManager.PopulateAssociationsForSet(ResourceSetWrapper resourceSet)
  at System.Data.Services.Serializers.MetadataSerializer.MetadataManager..ctor(DataServiceProviderWrapper provider, IDataService service)
  at System.Data.Services.Serializers.MetadataSerializer.GenerateMetadata(MetadataEdmSchemaVersion metadataEdmSchemaVersion, IDataService service)
  at System.Data.Services.Providers.DataServiceProviderWrapper.WriteMetadataDocument(MetadataSerializer serializer, XmlWriter writer, IDataService service)
  at System.Data.Services.Serializers.MetadataSerializer.WriteRequest(IDataService service)
  at System.Data.Services.ResponseBodyWriter.Write(Stream stream)}
  </code></pre>
  
  <p>The issue is happening when the <code>GetResourceAssociationSet</code> method in the class that implements <code>IDataServiceMetadataProvider</code> (see more details below) is called with a <code>ResourceSet</code> of a different type (<code>ResidentialCustomer</code>) than the passed in <code>ResourceType</code> (<code>Customer</code>):</p>
  
  <pre><code>public ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty) 
  { 
      return resourceProperty.CustomState as ResourceAssociationSet; 
  } 
  </code></pre>
  
  <p>This causes a failure in the .net library class method <code>ResourceAssociationSet.GetResourceAssociationSetEnd</code>, where it fails to find a <code>ResourceAssociationSetEnd</code>, subsequently causing the null reference exception.</p>
  
  <p>The class that implements <code>IServiceProvider</code> (see more details below) sets up the metadata.  It sets up the association between <code>Customer</code> and <code>User</code> as follows:</p>
  
  <pre><code>        ResourceAssociationSet customerUserListSet = new ResourceAssociationSet(
              "CustomerUserList",
              new ResourceAssociationSetEnd(
                  customerSet,
                  customer,
                  customerUserList
              ),
              new ResourceAssociationSetEnd(
                  userSet,
                  user,
                  userCustomer
              )
          );
          customerUserList.CustomState = customerUserListSet;
          userCustomer.CustomState = customerUserListSet;
          metadata.AddAssociationSet(customerUserListSet);
  </code></pre>
  
  <p>A <code>ResidentialCustomer</code> should have access to its list of <code>User</code>s just like a <code>Customer</code>.  An object that inherits another should be able to use the base associations.  I do not believe the solution is to add another association between <code>ResidentialCustomer</code> and <code>User</code>, and trying to do so leads to property conflicts or undefined properties.  <strong>What piece am I missing for setting up associations between objects that are inherited by other objects?</strong></p>
  
  <p><strong>Additional Details:</strong>
  The associated classes for the custom provider are as follows:</p>
  
  <p>An interface for <code>DataContext</code> classes such as:</p>
  
  <pre><code>public interface IODataContext
  {
      IQueryable GetQueryable(ResourceSet set); 
      object CreateResource(ResourceType resourceType);
      void AddResource(ResourceType resourceType, object resource);
      void DeleteResource(object resource);
      void SaveChanges();
  }
  </code></pre>
  
  <p>A class to implement <code>IDataServiceMetadataProvider</code> such as:</p>
  
  <pre><code>public class ODataServiceMetadataProvider : IDataServiceMetadataProvider
  {
      private Dictionary&lt;string, ResourceType&gt; resourceTypes = new Dictionary&lt;string, ResourceType&gt;();
      private Dictionary&lt;string, ResourceSet&gt; resourceSets = new Dictionary&lt;string, ResourceSet&gt;();
      private List&lt;ResourceAssociationSet&gt; _associationSets = new List&lt;ResourceAssociationSet&gt;(); 
  
      public string ContainerName
      {
          get { return "MyDataContext"; }
      }
  
       public string ContainerNamespace
       {
           get { return "MyNamespace"; }
       }
  
      public IEnumerable&lt;ResourceSet&gt; ResourceSets
      {
           get { return this.resourceSets.Values; }
      }
  
      public IEnumerable&lt;ServiceOperation&gt; ServiceOperations
      {
          get { yield break; }
      }
  
      public IEnumerable&lt;ResourceType&gt; Types
      {
          get { return this.resourceTypes.Values; }
      }
  
      public bool TryResolveResourceSet(string name, out ResourceSet resourceSet)
      {
          return resourceSets.TryGetValue(name, out resourceSet);
      }
  
      public bool TryResolveResourceType(string name, out ResourceType resourceType)
      {
          return resourceTypes.TryGetValue(name, out resourceType);
      }
  
      public bool TryResolveServiceOperation(string name, out ServiceOperation serviceOperation)
      {
          serviceOperation = null;
          return false;
      }
  
      public void AddResourceType(ResourceType type)
      {
          type.SetReadOnly();
          resourceTypes.Add(type.FullName, type);
      }
  
      public void AddResourceSet(ResourceSet set)
      {
          set.SetReadOnly();
          resourceSets.Add(set.Name, set);
      }
  
      public bool HasDerivedTypes(ResourceType resourceType)
      {
          if (resourceType.InstanceType == typeof(ResidentialCustomer))
          {
              return true;
          }
          return false;
      }
  
      public IEnumerable&lt;ResourceType&gt; GetDerivedTypes(ResourceType resourceType)
      {
          List&lt;ResourceType&gt; derivedResourceTypes = new List&lt;ResourceType&gt;();
          if (resourceType.InstanceType == typeof(ResidentialCustomer))
          {
              foreach (ResourceType resource in Types)
              {
                  if (resource.InstanceType == typeof(Customer))
                  {
                      derivedResourceTypes.Add(resource);
                  }
              }
          }
          return derivedResourceTypes;
      }
  
      public void AddAssociationSet(ResourceAssociationSet associationSet) 
      {
          _associationSets.Add(associationSet); 
      }
  
      public ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
      {
          return resourceProperty.CustomState as ResourceAssociationSet;
      }
  
      public ODataServiceMetadataProvider() { }
  }
  </code></pre>
  
  <p>A class to implement <code>IDataServiceQueryProvider</code> such as:</p>
  
  <pre><code>public class ODataServiceQueryProvider&lt;T&gt; : IDataServiceQueryProvider where T : IODataContext
  {
      T _currentDataSource;
      IDataServiceMetadataProvider _metadata;
  
      public object CurrentDataSource
      {
          get
          {
              return _currentDataSource;
          }
          set
          {
              _currentDataSource = (T)value;
          }
      }
  
      public bool IsNullPropagationRequired
      {
          get { return true; }
      }
  
      public object GetOpenPropertyValue(object target, string propertyName)
      {
          throw new NotImplementedException();
      }
  
      public IEnumerable&lt;KeyValuePair&lt;string, object&gt;&gt; GetOpenPropertyValues(object target)
      {
          throw new NotImplementedException();
      }
  
      public object GetPropertyValue(object target, ResourceProperty resourceProperty)
      {
          throw new NotImplementedException();
      }
  
      public IQueryable GetQueryRootForResourceSet(ResourceSet resourceSet)
      {
          return _currentDataSource.GetQueryable(resourceSet);
      }
  
      public ResourceType GetResourceType(object target)
      {
          Type type = target.GetType();
          return _metadata.Types.Single(t =&gt; t.InstanceType == type);
      }
  
      public object InvokeServiceOperation(ServiceOperation serviceOperation, object[] parameters)
      {
          throw new NotImplementedException();
      }
  
      public ODataServiceQueryProvider(IDataServiceMetadataProvider metadata)
      {
          _metadata = metadata;
      }
  }
  </code></pre>
  
  <p>A class to implement <code>IDataServiceUpdateProvider</code> such as:</p>
  
  <pre><code>public class ODataServiceUpdateProvider&lt;T&gt; : IDataServiceUpdateProvider where T : IODataContext
  {
      private IDataServiceMetadataProvider _metadata;
      private ODataServiceQueryProvider&lt;T&gt; _query;
      private List&lt;Action&gt; _actions;
  
      public T GetContext()
      {
          return ((T)_query.CurrentDataSource);
      }
  
      public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable&lt;KeyValuePair&lt;string, object&gt;&gt; concurrencyValues)
      {
          throw new NotImplementedException();
      }
  
      public void SetReference(object targetResource, string propertyName, object propertyValue)
      {
          _actions.Add(() =&gt; ReallySetReference(targetResource, propertyName, propertyValue));
      }
  
      public void ReallySetReference(object targetResource, string propertyName, object propertyValue)
      {
          targetResource.SetPropertyValue(propertyName, propertyValue);
      }
  
      public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
      {
          _actions.Add(() =&gt; ReallyAddReferenceToCollection(targetResource, propertyName, resourceToBeAdded));
      }
  
      public void ReallyAddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
      {
          var collection = targetResource.GetPropertyValue(propertyName);
          if (collection is IList)
          {
              (collection as IList).Add(resourceToBeAdded);
          }
      }
  
      public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
      {
          _actions.Add(() =&gt; ReallyRemoveReferenceFromCollection(targetResource, propertyName, resourceToBeRemoved));
      }
  
      public void ReallyRemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
      {
          var collection = targetResource.GetPropertyValue(propertyName);
          if (collection is IList)
          {
              (collection as IList).Remove(resourceToBeRemoved);
          }
      }
  
      public void ClearChanges()
      {
          _actions.Clear();
      }
  
      public void SaveChanges()
      {
          foreach (var a in _actions)
              a();
          GetContext().SaveChanges();
      }
  
      public object CreateResource(string containerName, string fullTypeName)
      {
          ResourceType type = null;
          if (_metadata.TryResolveResourceType(fullTypeName, out type))
          {
              var context = GetContext();
              var resource = context.CreateResource(type);
              _actions.Add(() =&gt; context.AddResource(type, resource));
              return resource;
          }
          throw new Exception(string.Format("Type {0} not found", fullTypeName));
      }
  
      public void DeleteResource(object targetResource)
      {
          _actions.Add(() =&gt; GetContext().DeleteResource(targetResource));
      }
  
      public object GetResource(IQueryable query, string fullTypeName)
      {
          var enumerator = query.GetEnumerator();
          if (!enumerator.MoveNext())
              throw new Exception("Resource not found");
          var resource = enumerator.Current;
          if (enumerator.MoveNext())
              throw new Exception("Resource not uniquely identified");
  
          if (fullTypeName != null)
          {
              ResourceType type = null;
              if (!_metadata.TryResolveResourceType(fullTypeName, out type))
                  throw new Exception("ResourceType not found");
              if (!type.InstanceType.IsAssignableFrom(resource.GetType()))
                  throw new Exception("Unexpected resource type");
          }
          return resource;
     }
  
      public object ResetResource(object resource)
      {
          _actions.Add(() =&gt; ReallyResetResource(resource));
          return resource;
      }
  
      public void ReallyResetResource(object resource)
      {
          var clrType = resource.GetType();
          ResourceType resourceType = _metadata.Types.Single(t =&gt; t.InstanceType == clrType);
          var resetTemplate = GetContext().CreateResource(resourceType);
  
          foreach (var prop in resourceType.Properties
                   .Where(p =&gt; (p.Kind &amp; ResourcePropertyKind.Key) != ResourcePropertyKind.Key))
          {
              var clrProp = clrType.GetProperties().Single(p =&gt; p.Name == prop.Name);
              var defaultPropValue = clrProp.GetGetMethod().Invoke(resetTemplate, new object[] { });
              clrProp.GetSetMethod().Invoke(resource, new object[] { defaultPropValue });
          }
      }
  
      public object ResolveResource(object resource)
      {
          return resource;
      }
  
      public object GetValue(object targetResource, string propertyName)
      {
          var value = targetResource.GetType().GetProperties().Single(p =&gt; p.Name == propertyName).GetGetMethod().Invoke(targetResource, new object[] { });
          return value;
      }
  
      public void SetValue(object targetResource, string propertyName, object propertyValue)
      {
          targetResource.GetType().GetProperties().Single(p =&gt; p.Name == propertyName).GetSetMethod().Invoke(targetResource, new[] { propertyValue });
       }
  
       public ODataServiceUpdateProvider(IDataServiceMetadataProvider metadata, ODataServiceQueryProvider&lt;T&gt; query)
       {
           _metadata = metadata;
           _query = query;
           _actions = new List&lt;Action&gt;();
      }
  }
  </code></pre>
  
  <p>A class to implement <code>IServiceProvider</code> such as:</p>
  
  <pre><code>public class ODataService&lt;T&gt; : DataService&lt;T&gt;, IServiceProvider where T : IODataContext
  {
      private ODataServiceMetadataProvider _metadata;
      private ODataServiceQueryProvider&lt;T&gt; _query;
      private ODataServiceUpdateProvider&lt;T&gt; _updater;
  
      public object GetService(Type serviceType)
      {
          if (serviceType == typeof(IDataServiceMetadataProvider))
          {
              return _metadata;
          }
          else if (serviceType == typeof(IDataServiceQueryProvider))
          {
              return _query;
          }
          else if (serviceType == typeof(IDataServiceUpdateProvider))
          {
              return _updater;
          }
          else
          {
              return null;
          }
      }
  
      public ODataServiceMetadataProvider GetMetadataProvider(Type dataSourceType)
      {
          ODataServiceMetadataProvider metadata = new ODataServiceMetadataProvider();
          ResourceType customer = new ResourceType(
              typeof(Customer),
              ResourceTypeKind.EntityType,
              null,
              "MyNamespace",
              "Customer",
              false
          );
          ResourceProperty customerCustomerID = new ResourceProperty(
              "CustomerID",
              ResourcePropertyKind.Key |
              ResourcePropertyKind.Primitive,
              ResourceType.GetPrimitiveResourceType(typeof(Guid))
          );
          customer.AddProperty(customerCustomerID);
          ResourceProperty customerCustomerName = new ResourceProperty(
              "CustomerName",
              ResourcePropertyKind.Primitive,
              ResourceType.GetPrimitiveResourceType(typeof(string))
          );
          customer.AddProperty(customerCustomerName);
          ResourceType residentialCustomer = new ResourceType(
              typeof(ResidentialCustomer),
              ResourceTypeKind.EntityType,
              customer,
              "MyNamespace",
              "ResidentialCustomer",
              false
          );
          ResourceType user = new ResourceType(
              typeof(User),
              ResourceTypeKind.EntityType,
              null,
              "MyNamespace",
              "User",
              false
          );
          ResourceProperty userUserID = new ResourceProperty(
              "UserID",
              ResourcePropertyKind.Key |
              ResourcePropertyKind.Primitive,
              ResourceType.GetPrimitiveResourceType(typeof(Guid))
          );
          user.AddProperty(userUserID);
          ResourceProperty userCustomerID = new ResourceProperty(
              "CustomerID",
              ResourcePropertyKind.Primitive,
              ResourceType.GetPrimitiveResourceType(typeof(Guid))
          );
          user.AddProperty(userCustomerID);
          ResourceProperty userEmailAddress = new ResourceProperty(
              "EmailAddress",
              ResourcePropertyKind.Primitive,
              ResourceType.GetPrimitiveResourceType(typeof(string))
          );
          user.AddProperty(userEmailAddress);
  
          var customerSet = new ResourceSet("Customers", customer);
          var residentialCustomerSet = new ResourceSet("ResidentialCustomers", residentialCustomer);
          var userSet = new ResourceSet("Users", user);
  
          var userCustomer = new ResourceProperty(
              "Customer",
              ResourcePropertyKind.ResourceReference,
              customer
          );
          user.AddProperty(userCustomer);
  
          var customerUserList = new ResourceProperty(
              "UserList",
              ResourcePropertyKind.ResourceSetReference,
              user
          );
          customer.AddProperty(customerUserList);
  
          metadata.AddResourceType(customer);
          metadata.AddResourceSet(customerSet);
          metadata.AddResourceType(residentialCustomer);
          metadata.AddResourceSet(residentialCustomerSet);
          metadata.AddResourceType(user);
          metadata.AddResourceSet(userSet);
  
          ResourceAssociationSet customerUserListSet = new ResourceAssociationSet(
              "CustomerUserList",
              new ResourceAssociationSetEnd(
                  customerSet,
                  customer,
                  customerUserList
              ),
              new ResourceAssociationSetEnd(
                  userSet,
                  user,
                  userCustomer
              )
          );
          customerUserList.CustomState = customerUserListSet;
          userCustomer.CustomState = customerUserListSet;
          metadata.AddAssociationSet(customerUserListSet);
  
          return metadata;
      }
  
      public ODataServiceQueryProvider&lt;T&gt; GetQueryProvider(ODataServiceMetadataProvider metadata)
      {
          return new ODataServiceQueryProvider&lt;T&gt;(metadata);
      }
  
      public ODataServiceUpdateProvider&lt;T&gt; GetUpdateProvider(ODataServiceMetadataProvider metadata, ODataServiceQueryProvider&lt;T&gt; query)
      {
          return new ODataServiceUpdateProvider&lt;T&gt;(metadata, query);
      }
  
      public ODataService()
      {
          _metadata = GetMetadataProvider(typeof(T));
          _query = GetQueryProvider(_metadata);
          _updater = GetUpdateProvider(_metadata, _query);
      }
  }
  </code></pre>
  
  <p>The <code>DataContext</code> class holds the CLR collections and wires up the service operations such as:</p>
  
  <pre><code>public partial class MyDataContext: IODataContext
  {
      private List&lt;Customer&gt; _customers = null;
      public List&lt;Customer&gt; Customers
      {
          get
          {
              if (_customers == null)
              {
                  _customers = DataManager.GetCustomers);
              }
              return _customers;
          }
      }
  
      private List&lt;ResidentialCustomer&gt; _residentialCustomers = null;
      public List&lt;ResidentialCustomer&gt; ResidentialCustomers
      {
          get
          {
              if (_residentialCustomers == null)
              {
                  _residentialCustomers = DataManager.GetResidentialCustomers();
              }
              return _residentialCustomers;
          }
      }
  
      private List&lt;User&gt; _users = null;
      public List&lt;User&gt; Users
      {
          get
          {
              if (_users == null)
              {
                  _users = DataManager.GetUsers();
              }
              return _users;
          }
      }
  
      public IQueryable GetQueryable(ResourceSet set)
      {
          if (set.Name == "Customers") return Customers.AsQueryable();
          if (set.Name == "ResidentialCustomers") return ResidentialCustomers.AsQueryable();
          if (set.Name == "Users") return Users.AsQueryable();
          throw new NotSupportedException(string.Format("{0} not found", set.Name));
      }
  
      public object CreateResource(ResourceType resourceType)
      {
          if (resourceType.InstanceType == typeof(Customer))
          {
              return new Customer();
          }
          if (resourceType.InstanceType == typeof(ResidentialCustomer))
          {
              return new ResidentialCustomer();
          }
          if (resourceType.InstanceType == typeof(User))
          {
              return new User();
          }
          throw new NotSupportedException(string.Format("{0} not found for creating.", resourceType.FullName));
      }
  
      public void AddResource(ResourceType resourceType, object resource)
      {
          if (resourceType.InstanceType == typeof(Customer))
          {
              Customer i = resource as Customer;
              if (i != null)
              {
                  Customers.Add(i);
                  return;
              }
          }
          if (resourceType.InstanceType == typeof(ResidentialCustomer))
          {
              ResidentialCustomeri = resource as ResidentialCustomer;
              if (i != null)
              {
                  ResidentialCustomers.Add(i);
                  return;
              }
          }
          if (resourceType.InstanceType == typeof(User))
          {
              Useri = resource as User;
              if (i != null)
              {
                  Users.Add(i);
                  return;
              }
          }
          throw new NotSupportedException(string.Format("{0} not found for adding.", resourceType.FullName));
      }
  
      public void DeleteResource(object resource)
      {
          if (resource.GetType() == typeof(Customer))
          {
              Customers.Remove(resource as Customer);
              return;
          }
          if (resource.GetType() == typeof(ResidentialCustomer))
          {
              ResidentialCustomers.Remove(resource as ResidentialCustomer);
              return;
          }
          if (resource.GetType() == typeof(User))
          {
              Users.Remove(resource as User);
              return;
          }
          throw new NotSupportedException(string.Format("{0} not found for deletion.", resource.GetType().FullName));
      }
  
      public void SaveChanges()
      {
          foreach (var item in Customers.Where(i =&gt; i.IsModified == true))
              item.Save();
          foreach (var item in ResidentialCustomers.Where(i =&gt; i.IsModified == true))
              item.Save();
          foreach (var item in Users.Where(i =&gt; i.IsModified == true))
              item.Save();
      }
  }
  </code></pre>
  
  <p>The data service uses the custom data service class and the data context, such as:</p>
  
  <pre><code>public class MyDataService : ODataService&lt;MyDataContext&gt;
  {
      public static void InitializeService(DataServiceConfiguration config)
      {
          config.SetEntitySetAccessRule("Customers", EntitySetRights.All);
          config.SetEntitySetAccessRule("ResidentialCustomers", EntitySetRights.All);
          config.SetEntitySetAccessRule("Users", EntitySetRights.All);
          config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
          config.DataServiceBehavior.AcceptProjectionRequests = true; 
      }
  }
  </code></pre>