7

I'm implementing a custom data provider, I have gotten it to the point that it returns data and can be filtered, but am having some trouble getting relationships to work.

When querying the metadata the relationships look correct, and when querying a table the related property links appear, but when attempting to access a ResourceReference property I get the following exception:

    Object reference not set to an instance of an object.
        System.NullReferenceException
        stacktrace at System.Data.Services.Providers.DataServiceProviderWrapper.GetResourceAssociationSet(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
   at System.Data.Services.Providers.DataServiceProviderWrapper.GetContainer(ResourceSetWrapper sourceContainer, ResourceType sourceResourceType, ResourceProperty navigationProperty)
   at System.Data.Services.Providers.DataServiceProviderWrapper.GetResourceProperties(ResourceSetWrapper resourceSet, ResourceType resourceType)
   at System.Data.Services.Serializers.SyndicationSerializer.WriteObjectProperties(IExpandedResult expanded, Object customObject, ResourceType resourceType, Uri absoluteUri, String relativeUri, SyndicationItem item, DictionaryContent content, EpmSourcePathSegment currentSourceRoot)
   at System.Data.Services.Serializers.SyndicationSerializer.WriteEntryElement(IExpandedResult expanded, Object element, ResourceType expectedType, Uri absoluteUri, String relativeUri, SyndicationItem target)
   at System.Data.Services.Serializers.SyndicationSerializer.WriteTopLevelElement(IExpandedResult expanded, Object element)
   at System.Data.Services.Serializers.Serializer.WriteRequest(IEnumerator queryResults, Boolean hasMoved)
   at System.Data.Services.ResponseBodyWriter.Write(Stream stream)

Here's a sample of how I create the relationships:

var sourceReference = new ResourceProperty(
                relatedType.ResourceTypeName,
                ResourcePropertyKind.ResourceReference,
                relatedType.ResourceType);
            sourceReference.CanReflectOnInstanceTypeProperty = false;
            compoundType.ResourceType.AddProperty(sourceReference);

var destinationReference = new ResourceProperty(
                compoundType.ResourceSetName,
                ResourcePropertyKind.ResourceSetReference,
                compoundType.ResourceType);
            destinationReference.CanReflectOnInstanceTypeProperty = false;
            source.ResourceType.AddProperty(destinationReference);

var sourceAssociation = new ResourceAssociationSet(
                        "source",
                        new ResourceAssociationSetEnd(compoundType.ResourceSet, compoundType.ResourceType, sourceReference),
                        new ResourceAssociationSetEnd(relatedType.ResourceSet, relatedType.ResourceType, null));

var destinationAssociation = new ResourceAssociationSet(
                             "destination",
                             new ResourceAssociationSetEnd(relatedType.ResourceSet, relatedType.ResourceType, destinationReference),
                             new ResourceAssociationSetEnd(compoundType.ResourceSet, compoundType.ResourceType, null));

From looking at the sample code on the OData website I thought I'd done it all correctly, and cannot determine my error. Any ideas? or tips on debugging a custom WCF Data service?

Update: Here's what happens just before the null exception.

Have a resource set Collars with a relationship to Projects so I do this query: blah.svc/Collars(1)/Project

My override of GetResourceAssociationSet in my IDataServiceMetadataProvider gets called with the parameters ResourceSet = Collars, ResourceType = Collar, Property = Project and I return the association set specified above. GetResourceAssociationSet is then called again with ResourceSet = Projects, ResourceType = Collar, Property = Project and I return the same association set.

Then in System.Data.Services.Providers.GetResourceAssociationSetEnd the variables passed in are resourceSet = Projects, resourceType = Collar, resourceProperty = Project, this function returns null.

Which makes thisEnd in System.Data.Services.Providers.DataServiceProviderWrapper.GetResourceAssociationSet equal to null. Then GetRelatedResourceAssociationSetEnd is called with the same variables and also returns null.

So it then crashes with the call

ResourceSetWrapper relatedSet = this.ValidateResourceSet(relatedEnd.ResourceSet); 

because relatedEnd is null.

Sigh
  • 659
  • 13
  • 30
  • In case of bidirectional relationships you can create just one ResourceAssociationSet and return it for both navigation properties. But that's just an optimization, the above should work as well. – Vitek Karas MSFT Oct 05 '11 at 10:32
  • I can't find what's wrong unfortunately. Can you try to either enable source code for the .NET framework so that you see exactly where it fails. Or if that's not possible the debugger should show you at least some local variables when the exception is thrown, can you please post those as well (and theirs values)? – Vitek Karas MSFT Oct 05 '11 at 10:32
  • Thanks Vitek, I've added some more detail above but I am struggling to get source code debugging to work :( – Sigh Oct 06 '11 at 03:46
  • Yay, deleted my symbol cached and redownloaded them all, and source debugging works again, more detail added. – Sigh Oct 06 '11 at 06:39
  • It might be the linq provider. I think I had a similar exception when I tried using Nhibernate.Linq 2.0. The only way I could fix it was using another provider :( – Jason Freitas Oct 07 '11 at 02:11
  • I have written my own provider, but it isn't getting far enough to hit it. It's crashing while trying to get the relationships, well before querying the actual data. – Sigh Oct 07 '11 at 05:15
  • I just encountered the same error. I'd really like to see this problem resolved... Seems like a MS bug, but hopefully there is a workaround. I'll start a bounty... – Jeff Oct 07 '11 at 14:36
  • In the above sample in the code you have relatedType and compoundType and then later you talk about Collar and Project. Could you please map one to the other? Also if the $metadata works, could you please post the output of it here, or at least some representative sample? – Vitek Karas MSFT Oct 07 '11 at 20:04
  • Also if you can and you have a repro project, you could mail it to vitkaras at microsoft dot com and I can take a look in the debugger. – Vitek Karas MSFT Oct 07 '11 at 20:05
  • One other thing in the above sample which confuses me, you have a relatedType to which you add a navigation property pointing to compoundType. But that you add navigation property pointing back to the relatedType to a type called source. I would expect that to be added to the compoundType (or maybe they are the same thing). – Vitek Karas MSFT Oct 07 '11 at 20:09
  • 1
    In any case, if you could dumpt the ResourceProperty representing the navigation property for each of the directions that would help. The GetResourceAssociationSet call originates with a ResourceType, for which the base ResourceType is taked and one of its ResourceProperties (for the navigation property in question) us used. These three are then passed to the GetResourceAssociationSet call. So the values you descibe above would lead me to believe that your metadata is not correctly setup, but it's hard to confirm that from the above. – Vitek Karas MSFT Oct 07 '11 at 20:19
  • Thanks for the reply Vitek. I did end up finding the answer to the issue. Although the behavior desired is not supported (in my case), it's certainly a bug in the Data Services implementation to behave as such. I've emailed my code to your provided email address. If it's worth noting - the reason I've implemented a custom WCF Data Services Provider as opposed to using the built in reflection provider is because Microsoft chose to remove the functionality for $expand via the BasicExpandProvider (internal) in the reflection provider. – Jeff Oct 08 '11 at 22:03
  • There is an explicit check in the MS source code that will not include expand segments into the expression tree if using the reflection provider. I DO want these expansions in the expression tree...and so the only way to get them is with my own reflection provider... – Jeff Oct 08 '11 at 22:04
  • Thanks @VitekKarasMSFT, following your comments I was able to track it down to a mistake in the implementation of GetResourceType in my IDataServiceQueryProvider, which when querying a relationship was returning the parent type and not the related type. – Sigh Oct 25 '11 at 09:01

2 Answers2

2

Well, in my debugging I noticed the last time GetResourceAssociationSet was called before the error occurred was for a case where the resourceSet and resourceType parameters had different values (in the case of a derived type).

So, I looked and found this

WCF data services (OData), query with inheritance limitation?

...and lo and behold this uninformative null reference exception (at least in my case) is caused by that same issue. I removed the offending property (and then moved it to the base resource set, even though it doesn't exist there in practice), and the issue was resolved.

Interesting side note (in case this helps the original poster): ResourceType.Properties includes properties from Base Types. Had to change some code to use PropertiesDeclaredOnThisType instead...

Community
  • 1
  • 1
Jeff
  • 35,755
  • 15
  • 108
  • 220
  • Jeff, could you elaborate on your issue and solution? I'm running into a similar inheritance issue (http://stackoverflow.com/questions/9182999/having-trouble-enabling-inheritance-and-associations-with-a-custom-odata-service). The link to the other similar issue was for an EF provider, does the same inheritance limitation (still) exist for custom providers? – Dave Clemmer Feb 08 '12 at 03:24
  • Yes, it does exist even for custom providers (like mine). Ultimately we abandoned WCF Data Services and moved to a more standard web services approach. It's a cool technology, but not really well thought out or implemented, in my humble opinion. If you really want IQueryable over web services support, I'd suggest looking into expression tree serialization or passing and parsing a LINQ query string via rest. – Jeff Feb 08 '12 at 14:17
  • Thanks Jeff, I'm on my way towards using a REST web service directly for this. – Dave Clemmer Feb 08 '12 at 16:49
-1

The solution for me was that I had made a mistake in my implementation of GetResourceType in my IDataServiceQueryProvider.

When querying a Resource Property that was a ResourceSetReference I was returning the ResourceType of the parent resource, and not the type of the related resource.

Sigh
  • 659
  • 13
  • 30