LINQ-to-Entities 中的类型成员支持?
我有一个使用实体框架模型的 MVC3 项目,在其中我标记了一个这样的类:
public partial class Product
{
public bool IsShipped
{
get { /* do stuff */ }
}
}
并且我想在 LINQ 表达式中使用它:
db.Products.Where(x => x.IsShipped).Select(...);
但是,我收到以下错误:
System.NotSupportedException 未由用户代码 Message=The 处理 LINQ to Entities 不支持指定类型成员“IsShipped”。 仅初始值设定项、实体成员和实体导航属性 都支持。来源=System.Data.Entity
我用谷歌搜索过,但没有找到任何关于我尝试过的用法的明确信息:
public partial class Product
{
public bool IsShipped()
{
/* do stuff */
}
}
db.Products.Where(x => x.IsShipped()).Select(...);
但后来我得到:
System.NotSupportedException 未由用户代码 Message=LINQ 处理 to Entities 无法识别“Boolean IsShipped()”方法, 并且此方法无法转换为存储表达式。
来源=System.Data.Entity
有一些功能我不想构建到 LINQ 查询本身中...处理这个问题的好方法是什么?
* update *
Darin 提出了一个有效的观点:IsShipped
的实现中所做的任何事情都需要转换为 SQL 查询,而编译器可能不知道如何转换这样做,因此将所有对象检索到内存中似乎是唯一的选择(除非直接查询数据库)。我这样尝试:
IEnumerable<Product> xp = db.Quizes
.ToList()
.Where(x => !x.IsShipped)
.Select(x => x.Component.Product);
但它会生成此错误:
发生了关系多重性约束违规: EntityReference 只能有一个相关对象,但是 查询返回多个相关对象。这是不可恢复的 错误。
奇怪的是,这确实有效:
IEnumerable<Product> xp = db.Quizes
.ToList()
.Where(x => x.Skill.Id == 3)
.Select(x => x.Component.Product);
为什么会这样呢?
* 更新 II *
抱歉,最后一条语句也不起作用...
* 更新 III *
我将结束此问题,以寻求此处建议的解决方案将我的逻辑扁平化为查询 - 讨论将转移到 这个新帖子。第二种选择,将整个原始查询检索到内存中,可能是不可接受的,但第三种选择,将逻辑实现为对数据库的直接查询,仍有待探索。
感谢大家的宝贵意见。
I have an MVC3 project using the Entity Framework model in which I've marked up a class like this:
public partial class Product
{
public bool IsShipped
{
get { /* do stuff */ }
}
}
and which I want to use in a LINQ expression:
db.Products.Where(x => x.IsShipped).Select(...);
however, I get the following error:
System.NotSupportedException was unhandled by user code Message=The
specified type member 'IsShipped' is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties
are supported. Source=System.Data.Entity
I've googled but not found anything definitive about this usage to I tried:
public partial class Product
{
public bool IsShipped()
{
/* do stuff */
}
}
db.Products.Where(x => x.IsShipped()).Select(...);
but then I get:
System.NotSupportedException was unhandled by user code Message=LINQ
to Entities does not recognize the method 'Boolean IsShipped()' method,
and this method cannot be translated into a store expression.
Source=System.Data.Entity
there's functionality there that I don't want to build into the LINQ query itself... what's a good way to handle this?
* update *
Darin makes the valid point that whatever is done in the implementation of IsShipped
would need to be converted to a SQL query and the compiler probably doesn't know how to do it, thus retrieving all objects into memory seems the only choice (unless a direct query to the database is made). I tried it like this:
IEnumerable<Product> xp = db.Quizes
.ToList()
.Where(x => !x.IsShipped)
.Select(x => x.Component.Product);
but it generates this error:
A relationship multiplicity constraint violation occurred: An
EntityReference can have no more than one related object, but the
query returned more than one related object. This is a non-recoverable
error.
though curiously this works:
IEnumerable<Product> xp = db.Quizes
.ToList()
.Where(x => x.Skill.Id == 3)
.Select(x => x.Component.Product);
why would that be?
* update II *
sorry, that last statement doesn't work either...
* update III *
I'm closing this question in favour of pursuing a solution as suggested here to flatten my logic into a query - the discussion will move to this new post. The second alternative, to retrieve the entire original query into memory, is likely unacceptable, but the third, of implementing the logic as a direct query to the database, remain to be explored.
Thanks everyone for the valuable input.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使此“DRY”的唯一方法(避免再次重复
Where
子句中IsShipped
内部的逻辑)并避免在应用过滤器之前将所有数据加载到内存中就是将IsShipped
的内容提取到一个表达式中。然后,您也可以将此表达式用作Where
和IsShipped
的参数。示例:您可以像这样执行查询:
这里您只有一个地方可以放置逻辑 (
IsShippedExpression
),然后将其用于数据库查询和您的IsShipped
> 财产也是如此。我会这样做吗?在大多数情况下可能不会,因为编译表达式很慢。除非逻辑非常复杂,可能会发生变化,并且我处于使用
IsShipped
的性能并不重要的情况下,否则我会重复该逻辑。总是可以将常用的过滤器提取到扩展方法中:然后以这种方式使用它:
虽然维护逻辑,但您将有两个地方:
IsShipped
属性和扩展方法,但是您可以重复使用它。The only way to make this "DRY" (avoid repeating the logic inside of
IsShipped
in theWhere
clause again) and to avoid loading all data into memory before you apply the filter is to extract the content ofIsShipped
into an expression. You can then use this expression as parameter toWhere
and inIsShipped
as well. Example:The you can perform the query like so:
Here you would have only one place to put the logic in (
IsShippedExpression
) and then use it for database queries and in yourIsShipped
property as well.Would I do this? In most cases probably no, because compiling the expression is slow. Unless the logic is very complex, likely a subject to change and I am in a situation where the performance of using
IsShipped
doesn't matter, I would repeat the logic. It's always possible to extract often used filters into an extension method:And then use it this way:
You would have two places though the maintain the logic: the
IsShipped
property and the extension method, but then you can reuse it.我猜
IsShipped
没有映射到数据库中的字段?这可以解释为什么 Linq to Entities 会抱怨——它无法基于此属性构造 sql 语句。您的
/* do stuff */
是否基于数据库中的字段?如果是这样,您可以在.Where()
中使用该逻辑。I'm guessing
IsShipped
is not mapped to a field in the database? That would explain why Linq to Entities complains - it cannot construct a sql statement based on this property.Is your
/* do stuff */
within the property based on fields that are in the database? If so, you could use that logic in your.Where()
.您可以首先通过调用
.ToList()
来使用结果,然后在客户端执行过滤器:当然,您应该意识到,通过这样做,您可能会降低应用程序的性能,因为数据库在这方面做得最好。
You could first consume the result by calling
.ToList()
and then perform the filter on the client side:Of course you should be aware that by doing this you are probably slowing the performance of your application down as databases are doing this best.
我假设您的意思是您想要执行与数据库无关的查询。但你的代码不符合你的意图。看这一行:
db.Products
部分表示您要查询数据库。要解决此问题,请首先在内存中获取实体集。然后,您可以在其上使用 Linq to Objects:
.ToList()
完成您的初始数据库查询,并为您提供内存中的表示形式,以便您根据自己的喜好使用和修改。之后,您可以使用非数据库属性。请注意,如果您在
ToList
之后执行进一步的数据库操作(例如编辑实体上的数据库属性、查询导航属性等),那么您将回到 Linq to Entities 界面,并且不再能够执行 Linq to Objects 操作。你不能直接混合两者。请注意,如果
public bool IsShipped()
读取或写入数据库属性或导航属性,如果您不小心,您可能会再次进入 Linq to Entities。I assume you mean you want to perform queries that don't have anything to do with the DB. But your code doesn't match your intention. Look at this line:
The part that says
db.Products
means you want to query the DB.To fix this, get an entity set in memory first. Then you can use Linq to Objects on it instead:
The
.ToList()
finishes off your initial DB query, and gives you an in-memory representation to work with and modify to your liking. After that point you can work with non-DB properties.Be careful that if you do further DB operations after
ToList
(such as editing DB properties on entities, querying off navigation properties, etc), then you'll be back in Linq to Entities land and will no longer be able to do Linq to Objects operations. You can't directly mix the two.And note that if
public bool IsShipped()
reads or writes DB properties or navigation properties, you might end up in Linq to Entities again if you're not careful.