【问题标题】:Inner query inside Wcf Services query throws 'NotSupportedException'Wcf 服务查询中的内部查询抛出“NotSupportedException”
【发布时间】:2013-11-12 20:17:36
【问题描述】:

我有一个具有多对多关系的类,并且在正确过滤使用 Wcf 数据服务(通过 DataServiceQuery 类)进行的查询的结果时遇到问题。该服务公开了一个 Entity Framework 5 模型。

例如这个简单的查询:

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

这给了我一个NotSupportedException,在运行时:

[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

如果我只在那里投射数据,而不是过滤,像这样:

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

它按预期工作。

【问题讨论】:

    标签: c# linq entity-framework-5 odata wcf-data-services


    【解决方案1】:

    OData 的 $select 子句(Linq select 在 URL 中映射到的内容)不支持转换属性值的投影,至少在 OData v3 中是这样。投影更多地用于减少您需要使用的属性数量,而不是用于操作属性值。

    所以这很好并且支持:

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

    但是,一旦您将 .Where 子句放在 Allocations 上,就无法将其转换为 OData URL 语法。

    【讨论】:

    • 哦,好吧,是否有另一种结构会以某种方式产生我想要的结果?例如,我们只想要不匹配特定用户的第一个分配。这通过为每个设备返回单行数据来限制生成的 sql 很多,而不是因为 N 分配而返回 N 行。
    • 你能反过来构造查询吗?询问与过滤器匹配的所有分配,然后选择与每个分配关联的设备名称?还是您还需要没有分配的设备?
    • 在这种情况下,我确实需要每个设备,无论是否存在分配。这里的查询完全被过度简化了……我的原始查询从与设备的某些关系中返回至少十几个不同的属性。
    • 听起来您的查询可能超出了 OData 查询语言的范围。相反,您可能会考虑编写一个服务操作,以便复杂性在服务器端,然后您可以将需要从客户端更改的一些内容作为参数公开给操作。有关详细信息,请参阅here
    • 这确实是一个有效的解决方法。我知道服务操作是如何工作的,但直到现在才使用它们。也许现在是时候了;)
    【解决方案2】:

    尝试测试是否相等而不是分配

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

    注意双重 ==

    【讨论】:

    • 更正了问题上的错字,这不是问题。
    【解决方案3】:

    实际上你是在 where 语句中做赋值

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

    【讨论】:

    • 打错字了,抱歉。我还更新了异常文本以反映代码属性(它正在使用另一个测试字符串,请注意“==”一开始就在那里)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    • 1970-01-01
    相关资源
    最近更新 更多