尝试返回 IQueryable<MyType> 时出现转换错误

Posted

技术标签:

【中文标题】尝试返回 IQueryable<MyType> 时出现转换错误【英文标题】:Casting errors when attempting to return an IQueryable<MyType> 【发布时间】:2014-01-21 07:01:00 【问题描述】:

我有一个查询应该返回IQueryable&lt;MyType&gt;。代码如下所示:

public IQueryable<MyType> GetFooList()

    var query = (from x in dbContext.TableX
                 join y in dbContext.TableY on x.our_id equals y.our_id 
                 join z in dbContext.TableZ on y.our_id equals z.our_id 
                 join a in dbContext.TableA on z.other_id equals a.other_id 
                 where !string.IsNullOrEmpty(x.status)
                 select new
                 
                   (fields....)
                 )
                 .AsQueryable();
    IQueryable<MyType> result = (IQueryable<MyType>) query;
    return result;

在调用控制器操作中,我想过滤此列表以获取在运行时指定的值;要过滤的参数将在各种调用操作之间有所不同。例如:

List<MyType> FooList = Interface.GetFooList()
    .Where( specific conditions )
    .ToList();

在行设置result 上,引发异常:

用户代码未处理无效的强制转换异常

无法转换类型的对象 'System.Data.Entity.Infrastructure.DbQuery'1[f__AnonymousType9'9[System.String,System.Nullable`1[System.DateTime],System.String,System.String,System.String,System.String, System.Int32,System.Nullable'1[System.DateTime],System.Nullable'1[System.DateTime]]]' 输入'System.Linq.IQueryable'1[MyType]'。

所以我认为这是一个转换问题,并在调用 AsQueryable() 之前添加了 .Cast&lt;MyType&gt;()。这会产生一个不同的错误:

无法将类型“匿名类型”转换为类型“MyType”。 LINQ 到 实体仅支持转换 EDM 基元或枚举类型。

如果我不进行任何转换,这些错误会在调用操作而不是实体框架访问器中引发。

我已经尝试了所有链接的“类似问题”中的建议,但无济于事 - 错误不断来回出现。我什至尝试包含.Select(obj =&gt; new MyType() fields... ) 以摆脱匿名类型。那也没用。

我觉得我错过了一些很明显的东西。

编辑添加

我更新了代码以选择类型:select new MyType() fields...。这工作正常。然后调用方法抛出了一个 NotSupportedException,在我过滤结果并从查询中创建一个列表的那一行:

实体或复杂类型“MyType”不能在 LINQ to Entities 查询。

ETA2

我将 EF 表属性复制到了一个新类 MyTypeDTO。我用 MyTypeDTO 替换了对 MyType 的所有使用。我收到了这个错误:

LINQ to Entities 不支持指定的类型成员“our_id”。仅支持初始化程序、实体成员和实体导航属性。

这是 DTO 中的属性:

public int our_id  get; set; 

所以我删除了 get/set,重建并重新运行。不,我遇到了同样的错误。

【问题讨论】:

您在选择中创建了一个匿名类型,而不是您的预期返回类型,那部分肯定是不正确的。 使用select new MyType 而不是匿名的。那么你就不需要演员了 I even tried including .Select(obj =&gt; new MyType() fields... ) to get away from the anonymous type. That didn't work either. 有什么不好的?错误是什么? 让我取消注释该部分,重新​​运行它,然后发布结果。 (翻转沙漏。) 我认为这是将查询检索到 IQueryable&lt;MyTypeDTO&gt; 变量中,然后对局部变量执行 Where() 查询。我将不得不用一些数据进行测试;之后,我会用我的结果更新它。 【参考方案1】:

您是对的,您所有的转换都失败的原因是匿名类型(即您使用select new ... 构造创建的东西)无法转换为命名类型。

我什至尝试包含.Select(obj =&gt; new MyType() fields... ) 以摆脱匿名类型。那也没用。

你在正确的轨道上 - 这应该可以工作,假设 MyType 有适当的设置器:

 var query = from x in dbContext.TableX
             join y in dbContext.TableY on x.our_id equals y.our_id 
             join z in dbContext.TableZ on y.our_id equals z.our_id 
             join a in dbContext.TableA on z.other_id equals a.other_id 
             where !string.IsNullOrEmpty(x.status)
             select new MyType
             
                MyTypeField1 = a.Column1
             ,  MyTypeField2 = z.Column3
             ,  // ...and so on
             ; // No need to convert to IQueryable - this should produce the right type
return result;

无法在 LINQ to Entities 查询中构造实体或复杂类型 'MyType'

这是因为MyType 是一个映射实体。您不应该创建返回映射实体的投影,因此 EF 正确地限制了您这样做的能力。您应该能够投影到非映射类型(以创建所谓的 DTO - 数据传输对象)。

定义一个类MyTypeDto,它具有MyClass 的属性,但没有方法或映射。在方法的定义中使用MyClassDto。这应该可以解决问题。

【讨论】:

不,与我的第一次编辑相同的错误。不同之处在于,错误不再是复杂类型 (namespace).MyType,而是 EFModel.MyType(其中“EFModel”是实体框架模型)。 您对强类型 IQueryable 使用 DTO 的建议帮助我找到了解决方案。 :啤酒:【参考方案2】:

这个问题非常微妙。 MyType 中不存在查询的匿名类型中的字段之一。我在创建要在此方法中使用的视图模型时发现了这一点。

对于它的价值,视图模型工作得很好。例如:

public IQueryable<MyViewModel> GetFooList(int parameter)

    var query = (from x in dbContext.TableX
                 join y in dbContext.TableY on x.our_id equals y.our_id
                 join z in dbContext.TableZ on y.our_id equals z.our_id
                 join a in dbContext.TableA on z.other_id  equals a.other_id 
                 where x.their_id == parameter
                 select new MyViewModel()
                 
                    field1 = x.field1,
                    field2 = y.field2,
                    field3 = z.field3 //<= this field did not exist in MyType
                 
                 ).AsQueryable();

    return query;

【讨论】:

以上是关于尝试返回 IQueryable<MyType> 时出现转换错误的主要内容,如果未能解决你的问题,请参考以下文章

返回 IEnumerable<T> 与 IQueryable<T>

返回列表而不是IQueryable

在 IQueryable<Log> Web API 操作上返回 204 状态代码

使用 LINQ 的 IQueryable 左外连接的扩展方法

Enumerable.Empty<T>() 等效于 IQueryable

Enumerable.Empty ()相当于IQueryable