繁体   English   中英

在LINQ-to-Entities中键入成员支持?

[英]Type member support in LINQ-to-Entities?

我有一个使用Entity Framework模型的MVC3项目,我在其中标记了这样一个类:

public partial class Product
{
    public bool IsShipped
    {
        get { /* do stuff */ }
    }
}

我想在LINQ表达式中使用它:

db.Products.Where(x => x.IsShipped).Select(...);

但是,我收到以下错误:

用户代码未处理System.NotSupportedException消息= 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查询本身...什么是处理这个的好方法?

*更新*

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 *

我正在关闭这个问题,转而采用这里建议的解决方案,将我的逻辑压缩成一个查询 - 讨论将转移到这个新帖子 将整个原始查询检索到内存中的第二种方法可能是不可接受的,但第三种方法是将逻辑实现为对数据库的直接查询,仍有待探索。

感谢大家的宝贵意见。

使这个“DRY”(避免再次在Where子句中重复IsShipped中的逻辑)并避免在应用过滤器之前将所有数据加载到内存中的唯一方法是将IsShipped的内容提取到表达式中。 然后,您可以将此表达式用作WhereIsShipped参数。 例:

public partial class Product
{
    public int ProductId { get; set; }           // <- mapped to DB
    public DateTime? ShippingDate { get; set; }  // <- mapped to DB
    public int ShippedQuantity { get; set; }     // <- mapped to DB

    // Static expression which must be understood
    // by LINQ to Entities, i.e. translatable into SQL
    public static Expression<Func<Product, bool>> IsShippedExpression
    {
        get { return p => p.ShippingDate.HasValue && p.ShippedQuantity > 0; }
    }

    public bool IsShipped // <- not mapped to DB because readonly
    {
        // Compile expression into delegate Func<Product, bool>
        // and execute delegate
        get { return Product.IsShippedExpression.Compile()(this); }
    }
}

您可以像这样执行查询:

var result = db.Products.Where(Product.IsShippedExpression).Select(...).ToList();

在这里,您只有一个地方可以将逻辑放入( IsShippedExpression ),然后将其用于数据库查询和IsShipped属性。

我会这样做吗? 在大多数情况下可能没有,因为编译表达式很慢。 除非逻辑非常复杂,否则可能会发生变化,而且我处于使用IsShipped的性能无关紧要的情况下,我会重复逻辑。 始终可以将常用的过滤器提取到扩展方法中:

public static class MyQueryExtensions
{
    public static IQueryable<Product> WhereIsShipped(
        this IQueryable<Product> query)
    {
        return query.Where(p => p.ShippingDate.HasValue && p.ShippedQuantity >0);
    }
}

然后以这种方式使用它:

var result = db.Products.WhereIsShipped().Select(...).ToList();

虽然维护逻辑有两个地方: IsShipped属性和扩展方法,但是你可以重用它。

我猜IsShipped没有映射到数据库中的字段? 这可以解释为什么Linq to Entities会抱怨 - 它无法构建基于此属性的sql语句。

是你/* do stuff */根据在数据库领域内的财产? 如果是这样,您可以在.Where()使用该逻辑。

您可以先通过调用.ToList()使用结果,然后在客户端执行过滤:

var result = db.Products.ToList().Where(x => x.IsShipped).Select(...);

当然,您应该意识到,通过这样做,您可能会降低应用程序的性能,因为数据库正在做得最好。

那里有功能,我不想构建LINQ查询本身...什么是处理这个的好方法?

我假设您的意思是要执行与DB无关的查询。 但是你的代码与你的意图不符。 看看这一行:

db.Products.Where(x => x.IsShipped()).Select(...);

db.Products的部分意味着您要查询数据库。

要解决此问题,请先在内存中设置实体。 然后你可以使用Linq来对象:

List<Product> products = db.Products
    .Where(x => x.SomeDbField == someValue)
    .ToList();

// Todo: Since the DB doesn't know about IsShipped, set that info here

// ...

var shippedProducts = products
    .Where(x => x.IsShipped())
    .Select(...);

.ToList()完成初始数据库查询,并为您提供内存表示,以便根据您的喜好进行操作和修改。 在那之后,您可以使用非数据库属性。

请注意,如果您在ToList之后执行进一步的数据库操作(例如编辑实体上的数据库属性,查询导航属性等),那么您将返回Linq to Entities land并且将无法再对Linq执行操作操作。 你不能直接混合两者。

请注意,如果public bool IsShipped()读取或写入数据库属性或导航属性,如果您不小心,可能会再次使用Linq to Entities。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM