EntitySet的< T>。凡(我的predicate)引发NotSupportedException我的、LT、EntitySet、GT

2023-09-03 05:24:45 作者:超拽又好听的

修改:让我们试试这个了。这一次,我已经使用了AdventureWorks示例数据库,所以你都可以一起玩。这将排除任何疯狂我在我自己的数据库进行。这里有一个新的例子证明什么可行,什么我期望的工作(但没有)。任何人都可以解释为什么它不工作或暗示实现我的目标(重构出公共EX pression因此它可以在其他地方重复使用)?不同的方式

EDIT: Let's try this again. This time I've used the AdventureWorks sample database so you can all play along. This will rule out anything crazy I've done in my own database. Here's a new example demonstrating what works and what I would expect to work (but doesn't). Can anyone explain why it doesn't work or suggest a different way of achieving my goal (refactoring out the common expression so it can be reused elsewhere)?

using (AdventureWorksDataContext db = new AdventureWorksDataContext())
{
    // For simplicity's sake we'll just grab the first result.
    // The result should have the name of the SubCategory and an array of Products with ListPrice greater than zero.
    var result = db.ProductSubcategories.Select(subCategory => new
    {
        Name = subCategory.Name,
        ProductArray = subCategory.Products.Where(product => product.ListPrice > 0).ToArray()
    }).First();
    Console.WriteLine("There are {0} products in SubCategory {1} with ListPrice > 0.", result.ProductArray.Length, result.Name);
    // Output should say: There are 3 products in SubCategory Bib-Shorts with ListPrice > 0.

    // This won't work.  I want to pull the expression out so that I can reuse it in several other places.
    Expression<Func<Product, bool>> expression = product => product.ListPrice > 0;
    result = db.ProductSubcategories.Select(subCategory => new
    {
        Name = subCategory.Name,
        ProductArray = subCategory.Products.Where(expression).ToArray() // This won't compile because Products is an EntitySet<Product> and that doesn't have an overload of Where that accepts an Expression.
    }).First();
    Console.WriteLine("There are {0} products in SubCategory {1} with ListPrice > 0.", result.ProductArray.Length, result.Name);
}

&LT; /编辑&gt;

</Edit>

下面的LINQ to SQL工作正常:

The following LINQ to SQL works fine:

var result = from subAccount in db.SubAccounts
             select new ServiceTicket
             {
                 MaintenancePlans = subAccount.Maintenances.Where(plan => plan.CancelDate == null && plan.UpgradeDate == null).Select(plan => plan.ToString()).ToArray()
                 // Set other properties...
             };

不过,我想打出来传递给其中,的predicate因为它应用于整个code。但是,如果我试图传递一个定义predicate到其中,失败,如:

However, I want to break out the predicate passed to the Where since it's used throughout the code. But if I try and pass a defined predicate into the Where it fails, such as:

Func<DatabaseAccess.Maintenance, bool> activePlanPredicate = plan => plan.CancelDate == null && plan.UpgradeDate == null;
var result = from subAccount in db.SubAccounts
             select new ServiceTicket
             {
                 MaintenancePlans = subAccount.Maintenances.Where(activePlanPredicate).Select(plan => plan.ToString()).ToArray()
                 // Set other properties...
             };

这是没有意义的我。任何人能解释这是怎么回事? 的维护的类型是的EntitySet&LT; D​​atabaseAccess.Maintenance&GT; 。我得到的错误是:

This makes no sense to me. Can anyone explain what's going on? Maintenances is of type EntitySet<DatabaseAccess.Maintenance>. The error I get is:

System.NotSupportedException:   用于查询不受支持过载   运营商位置。

System.NotSupportedException: Unsupported overload used for query operator 'Where'..

修改:对于那些有兴趣,这里是反射器具有第一个(工作),例如使用优化设置为.NET 2.0:

EDIT: For those interested, here's what Reflector has for the first (working) example with Optimization set to .NET 2.0:

using (BugsDatabaseDataContext db = new BugsDatabaseDataContext())
{
    ParameterExpression CS$0$0001;
    ParameterExpression CS$0$0006;
    ParameterExpression CS$0$0010;
    return db.SubAccounts.Select<SubAccount, ServiceTicket>(Expression.Lambda<Func<SubAccount, ServiceTicket>>(
        Expression.MemberInit(
            Expression.New(
                (ConstructorInfo) methodof(ServiceTicket..ctor), 
                new Expression[0]), 
                new MemberBinding[] 
                { 
                    Expression.Bind(
                        (MethodInfo) methodof(ServiceTicket.set_MaintenancePlans), 
                        Expression.Call(
                            null, 
                            (MethodInfo) methodof(Enumerable.ToArray), 
                            new Expression[] 
                            { 
                                Expression.Call(
                                    null, 
                                    (MethodInfo) methodof(Enumerable.Select), 
                                    new Expression[] 
                                    { 
                                        Expression.Call(
                                            null, 
                                            (MethodInfo) methodof(Enumerable.Where), 
                                            new Expression[] 
                                            { 
                                                Expression.Property(CS$0$0001 = Expression.Parameter(typeof(SubAccount), "subAccount"), (MethodInfo) methodof(SubAccount.get_Maintenances)), 
                                                Expression.Lambda<Func<Maintenance, bool>>(
                                                    Expression.AndAlso(
                                                        Expression.Equal(
                                                            Expression.Property(CS$0$0006 = Expression.Parameter(typeof(Maintenance), "plan"), (MethodInfo) methodof(Maintenance.get_CancelDate)), 
                                                            Expression.Convert(Expression.Constant(null, typeof(DateTime?)), typeof(DateTime?)), false, (MethodInfo) methodof(DateTime.op_Equality)
                                                        ), 
                                                        Expression.Equal(
                                                            Expression.Property(CS$0$0006, (MethodInfo) methodof(Maintenance.get_UpgradeDate)), 
                                                            Expression.Convert(Expression.Constant(null, typeof(DateTime?)), typeof(DateTime?)), false, (MethodInfo) methodof(DateTime.op_Equality)
                                                        )
                                                    ), 
                                                    new ParameterExpression[] { CS$0$0006 }
                                                ) 
                                            }
                                        ), 
                                        Expression.Lambda<Func<Maintenance, string>>(
                                            Expression.Call(
                                                CS$0$0010 = Expression.Parameter(typeof(Maintenance), "plan"), 
                                                (MethodInfo) methodof(object.ToString), 
                                                new Expression[0]
                                            ), 
                                            new ParameterExpression[] { CS$0$0010 }
                                        ) 
                                    }
                                ) 
                            }
                        )
                    )
                }
            ), 
            new ParameterExpression[] { CS$0$0001 }
        )
    ).ToList<ServiceTicket>();
}

修改:该反射输出的第二个例子(使用predicate)主要是相似的。以 Enumerable.Where ,而不是传递一个防爆pression.Lambda 它通过防爆pression.Constant(activePlan predicate)

EDIT: The Reflector output for the second example (using a predicate) is mostly similar. The biggest difference being that, in the call to Enumerable.Where, rather than passing an Expression.Lambda it passes Expression.Constant(activePlanPredicate).

推荐答案

我不完全理解的LINQ到实体胆量,但有一个开源(可使用专有软件)工具包,专门用于帮助解决这个问题被称为LinqKit,链接关闭此奥赖利相关文章:

I don't fully understand the guts of Linq to Entities, but there is an Open Source (usable in proprietary software) toolkit specifically designed to help solve this problem, called LinqKit, linked off this O'Reilly-related article:

http://www.albahari.com/nutshell/$p$pdicatebuilder。 ASPX

由于我不完全理解的胆量,我只是说出来了:

Since I don't fully understand the guts, I'll just quote them:

实体框架的查询处理管道无法处理调用前pressions,这就是为什么你需要调用AsExpandable在查询中的第一个对象。通过调用AsExpandable,您激活它代替调用EX pressions用更简单的结构是实体框架可以理解LINQKit的前pression visitor类。

Entity Framework's query processing pipeline cannot handle invocation expressions, which is why you need to call AsExpandable on the first object in the query. By calling AsExpandable, you activate LINQKit's expression visitor class which substitutes invocation expressions with simpler constructs that Entity Framework can understand.

下面是直接链接到LinqKit 。

和这里是code类型,这个项目可以:

And here is the type of code that this project enables:

using LinqKit;

// ...

Expression<Func<Product, bool>> expression = product => product.ListPrice > 0;

var result = db.ProductSubcategories
    .AsExpandable() // This is the magic that makes it all work
    .Select(
        subCategory => new
        {
            Name = subCategory.Name,
            ProductArray = subCategory.Products
                // Products isn't IQueryable, so we must call expression.Compile
                .Where(expression.Compile())
        })
    .First();

Console.WriteLine("There are {0} products in SubCategory {1} with ListPrice > 0."
    , result.ProductArray.Count()
    , result.Name
    );

结果是:

有3个产品的子类别背带裤,短裤ListPrice> 0

T.t

There are 3 products in SubCategory Bib-Shorts with ListPrice > 0.

耶,也不例外,我们可以提取predicate!

Yay, no exception, and we can extract the predicate!

 
精彩推荐
图片推荐