1

I have a class with a many-many relationship and I'm having problems in properly filtering the results from a query made using Wcf Data Services (through the DataServiceQuery class). This service exposes an Entity Framework 5 model.

This simple query for instance:

from device in devices select new { DeviceName = device.Name, TestUsers = device .Allocations .Where(allocation => allocation.User == "testUser") } 

This gives me a NotSupportedException, at runtime:

[NotSupportedException: Constructing or initializing instances of the type <>f__AnonymousType0`4[System.String,System.String,System.String,System.Collections.Generic.IEnumerable`1[Mobiltec.M3S.Model.AllocInfo]] with the expression device.Allocations.Where(_allocation => (_allocation.User == "testUser")) is not supported.] System.Data.Services.Client.NonEntityProjectionAnalyzer.VisitMethodCall(MethodCallExpression m) +650 System.Data.Services.Client.ALinqExpressionVisitor.Visit(Expression exp) +456 System.Data.Services.Client.ALinqExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original) +107 System.Data.Services.Client.ALinqExpressionVisitor.VisitNew(NewExpression nex) +52 System.Data.Services.Client.NonEntityProjectionAnalyzer.VisitNew(NewExpression nex) +226 System.Data.Services.Client.ALinqExpressionVisitor.Visit(Expression exp) +552 System.Data.Services.Client.NonEntityProjectionAnalyzer.Analyze(Expression e, PathBox pb, DataServiceContext context) +285 System.Data.Services.Client.ProjectionAnalyzer.Analyze(LambdaExpression e, PathBox pb, DataServiceContext context) +226 System.Data.Services.Client.ProjectionAnalyzer.AnalyzeResourceExpression(LambdaExpression lambda, ResourceExpression resource, DataServiceContext context) +58 System.Data.Services.Client.ProjectionAnalyzer.Analyze(LambdaExpression le, ResourceExpression re, Boolean matchMembers, DataServiceContext context) +335 System.Data.Services.Client.ResourceBinder.AnalyzeProjection(MethodCallExpression mce, SequenceMethod sequenceMethod, Expression& e) +1000 System.Data.Services.Client.ResourceBinder.VisitMethodCall(MethodCallExpression mce) +149 System.Data.Services.Client.ALinqExpressionVisitor.Visit(Expression exp) +456 System.Data.Services.Client.ALinqExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original) +107 System.Data.Services.Client.ALinqExpressionVisitor.VisitMethodCall(MethodCallExpression m) +87 System.Data.Services.Client.ResourceBinder.VisitMethodCall(MethodCallExpression mce) +177 System.Data.Services.Client.ALinqExpressionVisitor.Visit(Expression exp) +456 System.Data.Services.Client.ResourceBinder.Bind(Expression e, DataServiceContext context) +57 System.Data.Services.Client.DataServiceQueryProvider.Translate(Expression e) +252 System.Data.Services.Client.DataServiceQuery`1.Translate() +37 System.Data.Services.Client.DataServiceRequest.GetQuerySetCount(DataServiceContext context) +77 System.Data.Services.Client.DataServiceQueryProvider.ReturnSingleton(Expression expression) +332 

If I only project data in there, instead of filtering, like this:

from device in devices select new { DeviceName = device.Name, AllocatedUsers = device .Allocations .Select(allocation => allocation.User) } 

It works as expected.

3 Answers 3

1

OData's $select clause (which is what the Linq select maps to in the URL) does not support projections that transform property values, at least in OData v3. Projections are used more to reduce the number of properties that you need to work with, rather than to manipulate the property values.

So this is fine and supported:

from device in devices select new { DeviceName = device.Name, TestUsers = device.Allocations } 

but as soon as you put the .Where clause on Allocations, there is no way to transform that into OData URL syntax.

Sign up to request clarification or add additional context in comments.

5 Comments

Oh well, is there another construct that would result in what I want somehow? For instance, we only want the first allocation that does not match a specific user. This limits the generated sql a LOT, by returning a single row of data for each device, instead of N rows because of the N allocations.
Could you construct the query the other way around? Ask for all allocations that match the filter and then select the device names associated with each allocation? Or do you also need the devices that have no allocations?
I do need every device in this case, regardless of the presence of allocations. The query here is completely oversimplified... my original query returns at least a dozen different properties from some relationships with the device.
It sounds like your query might be outside the scope of the OData query language. Instead, you might consider writing a service operation so that the complexity is on the server-side, and then you can expose the few things that need to change from the client as parameters to the operation. For more information, see here.
That's a valid workaround indeed. I'm aware of how service operations work, but never used them until now. Perhaps now is the time ;)
1

try to test for equality instead of assigning

.Where(allocation => allocation.User == "testUser") 

notice the double ==

1 Comment

Corrected the typo on the question, that is not the problem.
1

actually you are doing assignment in your where statement

from device in devices select new { DeviceName = device.Name, TestUser = device .Allocations .Where(allocation => allocation.User = "testUser") // use == for equality } 

1 Comment

That was a typo, sorry. I've also updated the exception text to reflect the code property (it was using another test string, notice that the "==" was there at first)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.