Search code examples
c#linqdictionarywcf-data-services

In WCF Data Servics, is ToDictionary broken when using objects with relationships?


I have 3 objects with the following relationships: Feature contains a List<> of FeatureGroup objects. FeatureGroup contains an Application object as a property.

I want to create a dictionary that allows a List of Application names to be retrieved for a given Feature (Applications share features, and I want to see what apps support what features).

Here is one bit of code that works correctly:

            Dictionary(Feature, List(string)) AppLookup = this.DataContext().Features
                .Select(x => new
                {
                    Feature = x,
                    AppNames = x.FeatureGroups
                        .Select(y => y.Application.ApplicationShortName)
                        .Distinct()
                        .ToList()
                })
                .ToDictionary(x => x.Feature, x => x.AppNames);

            var appsFound = viewModel.AppLookup
                .Where(x => x.Value.Count > 0)
                .ToList(); //This contains 27 which is the correct value

This is the original way I wanted to code it, but it doesn't seem to work unless I uncomment the 2 Expand() lines:

            Dictionary(Feature, List(string)) AppLookup = this.DataContext().Features
                //.Expand("FeatureGroups")
                //.Expand("FeatureGroups/Application")
                .ToDictionary(x => x, x => x.FeatureGroups
                    .Select(y => y.Application.ApplicationShortName)
                    .Distinct()
                    .ToList());

            var appsFound = viewModel.AppLookup
                .Where(x => x.Value.Count > 0)
                .ToList(); //This is 0 unless the above .Expand lines are uncommented

Solution

  • The way WCF DS Client works is to take the query you write in LINQ turn it into a URL, perform a request on that URL and then get you back the results. The problem is that it doesn't support all LINQ expressions. ToDictionary is one of them. So in the second sample all it seems is .Features. So it issues a request "~/Features".

    The behavior of ~/Features is to not return related entities, so the response only contains the features, but not the related feature groups. As a result you get back a list of features where each features doesn't have any feature groups.

    There are two ways to "fix" this (and you found both of them). One is to add .Expand("FeatureGroups") into the query which tells the client to append $expand=FeatureGroups to the URL and thus request to get the features and all their feature groups. As a result the query returns a list of features where each features as all its right feature groups filled as well.

    The other way to fix this is to add more into the LINQ expression to describe the intent that you want the feature groups to be loaded as well. Your first sample does that by adding projection (Select) and asking for something from the FeatureGroups. The client in order to get you the results needs to ask for the features groups though the $expand as well.

    So the above behavior is by design. ToDictionary is not seen as part of the query (note that it's defined on IEnumerable not on IQueryable) and as such the client does what you asked for.