5

我正在使用 OData Nuget 包开发 Web API 原型。我在使 LINQ to EF 查询正常工作时遇到了一些问题。

这是我的数据模型。它已被高度简化。

我正在尝试使用此 DTO 使查询正常工作:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<Order> Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
}

查询如下所示:

[Queryable]
public IQueryable<Product> Get()
{
    var productA = _context.ProductA
                            .Select(p => new Product
                                {
                                    Id = p.id,
                                    Name = p.name,
                                    Orders = p.ProductAOrders.Select(o => new Order
                                        {
                                            Id = o.OrderId,
                                            Date = o.Orders.Date,
                                        })
                                });

    var productB = _context.ProductB
                            .Select(p => new Product
                                {
                                    Id = p.Id,
                                    Name = p.Name,
                                    Orders = p.ProductBOrders.Select(o => new Order
                                        {
                                            Id = o.OrderId,
                                            Date = o.Orders.Date,
                                        })
                                });

    return productA.Union(productB);
}

当尝试联合这两个查询时,我收到此错误:

<Error><Message>An error has occurred.</Message><ExceptionMessage>The 'Distinct' operation cannot be applied to the collection ResultType of the specified argument.
Parameter name: argument</ExceptionMessage><ExceptionType>System.ArgumentException</ExceptionType><StackTrace>   at System.Data.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.ValidateDistinct(DbExpression argument)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.UnionTranslator.TranslateBinary(ExpressionConverter parent, DbExpression left, DbExpression right)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.BinarySequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.Convert()
   at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Objects.ObjectQuery.ToTraceString()
   at System.Data.Entity.Internal.Linq.InternalQuery`1.ToString()
   at System.Data.Entity.Infrastructure.DbQuery`1.ToString()
   at System.Convert.ToString(Object value, IFormatProvider provider)
   at System.Web.Http.Tracing.Tracers.HttpActionDescriptorTracer.&lt;ExecuteAsync&gt;b__2(TraceRecord tr, Object value)
   at System.Web.Http.Tracing.ITraceWriterExtensions.&lt;&gt;c__DisplayClass1b`1.&lt;&gt;c__DisplayClass1f.&lt;TraceBeginEndAsync&gt;b__13(TraceRecord traceRecord)
   at System.Web.Http.Tracing.SystemDiagnosticsTraceWriter.Trace(HttpRequestMessage request, String category, TraceLevel level, Action`1 traceAction)
   at System.Web.Http.Tracing.ITraceWriterExtensions.&lt;&gt;c__DisplayClass1b`1.&lt;TraceBeginEndAsync&gt;b__12(TResult result)
   at System.Threading.Tasks.TaskHelpersExtensions.&lt;&gt;c__DisplayClass3b`2.&lt;Then&gt;b__3a(Task`1 t)
   at System.Threading.Tasks.TaskHelpersExtensions.ThenImpl[TTask,TOuterResult](TTask task, Func`2 continuation, CancellationToken cancellationToken, Boolean runSynchronously)
</StackTrace></Error>

我可以返回 productA 或 productB - 但返回这 2 个查询的 Union 会导致上述明显错误。

对我可能做错的事情有任何想法吗?

4

3 回答 3

5

看起来像一个EF错误。我假设您正在尝试让 MEST(相同类型的多个实体集)工作。您可以尝试,而不是您的查询,

public IQueryable<Product> Get()
{
    var productA = _context.ProductA
                            .Select(p => new Product
                            {
                                Id = p.id,
                                Name = p.name,
                            });

    var productB = _context.ProductB
                            .Select(p => new Product
                            {
                                Id = p.Id,
                                Name = p.Name,
                            });

    return
        productA
        .Union(productB)
        .Select(p => new Product
        {
            Id = p.Id,
            Name = p.Name,
            Orders = _context.Orders
                    .Where(o => o.ProductA.Id == p.Id || o.ProductB.Id == p.Id)
                    .Select(o => new Order
                    {
                        Id = o.OrderId,
                        Date = o.Orders.Date,
                    })
        });
}

这个想法是将导航属性从联合中取出,然后再将它们添加回来。这只有在 Orders 有指向 ProductA 或 ProductB 的反向指针时才有效。

于 2013-03-21T06:44:31.390 回答
1

这似乎是设计使然(或限制)。对于集合操作(​​UNION、INTERSECT、EXCEPT),我们只允许模型类型和“平面”瞬态类型(即没有集合属性的行类型(例如为投影创建))。这里的解决方法是通过强制执行查询来在客户端上进行联合,而不是这样做:

var query3 = query1.Union(query2);

做这个:

var query3 = query1.ToList().Union(query2);
于 2013-03-21T18:10:00.073 回答
-1

刚刚尝试了这个查询......这似乎是使用导航属性的唯一方法。

var productA = _context.ProductA
                .Select(p => new Product
                {
                    Id = p.id,
                    Name = p.name,
                });

var productB = _context.ProductB
                .Select(p => new Product
                {
                    Id = p.Id,
                    Name = p.Name,
                });

return
    productA
    .Union(productB)
    .Select(p => new Product
        {
            Id = p.Id,
            Name = p.Name,
            Orders =   _context.ProductAOrders
                    .Where(x => x.ProductAId == p.Id)
                    .Select(o => new Order
                    {
                        Id = o.ProductAId,
                        Date = o.Orders.Date
                    })
                    .Union( _context.ProductBOrders
                            .Where(x => x.ProductBId == p.Id)
                            .Select(o => new Order
                            {
                                Id = o.ProductBId,
                                Date = o.Orders.Date
                            }))
        });

它导致此错误:

<Error><Message>An error has occurred.</Message><ExceptionMessage>The type 'API.Models.Product' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.</ExceptionMessage><ExceptionType>System.NotSupportedException</ExceptionType><StackTrace>   at System.Data.Objects.ELinq.ExpressionConverter.ValidateInitializerMetadata(InitializerMetadata metadata)
   at System.Data.Objects.ELinq.ExpressionConverter.MemberInitTranslator.TypedTranslate(ExpressionConverter parent, MemberInitExpression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding&amp; binding)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression&amp; source, DbExpressionBinding&amp; sourceBinding, DbExpression&amp; lambda)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SelectTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.Convert()
   at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Objects.ObjectQuery.ToTraceString()
   at System.Data.Entity.Internal.Linq.InternalQuery`1.ToString()
   at System.Data.Entity.Infrastructure.DbQuery`1.ToString()
   at System.Convert.ToString(Object value, IFormatProvider provider)
   at System.Web.Http.Tracing.Tracers.HttpActionDescriptorTracer.&lt;ExecuteAsync&gt;b__2(TraceRecord tr, Object value)
   at System.Web.Http.Tracing.ITraceWriterExtensions.&lt;&gt;c__DisplayClass1b`1.&lt;&gt;c__DisplayClass1f.&lt;TraceBeginEndAsync&gt;b__13(TraceRecord traceRecord)
   at System.Web.Http.Tracing.SystemDiagnosticsTraceWriter.Trace(HttpRequestMessage request, String category, TraceLevel level, Action`1 traceAction)
   at System.Web.Http.Tracing.ITraceWriterExtensions.&lt;&gt;c__DisplayClass1b`1.&lt;TraceBeginEndAsync&gt;b__12(TResult result)
   at System.Threading.Tasks.TaskHelpersExtensions.&lt;&gt;c__DisplayClass3b`2.&lt;Then&gt;b__3a(Task`1 t)
   at System.Threading.Tasks.TaskHelpersExtensions.ThenImpl[TTask,TOuterResult](TTask task, Func`2 continuation, CancellationToken cancellationToken, Boolean runSynchronously)</StackTrace></Error>

我无法理解为什么“API.Models.Product”出现在单个 LINQ to Entities 查询中的两个结构不兼容的初始化中。

如果我更换

        Orders =   _context.ProductAOrders
                .Where(x => x.ProductAId == p.Id)
                .Select(o => new Order
                {
                    Id = o.ProductAId,
                    Date = o.Orders.Date
                })
                .Union( _context.ProductBOrders
                        .Where(x => x.ProductBId == p.Id)
                        .Select(o => new Order
                        {
                            Id = o.ProductBId,
                            Date = o.Orders.Date
                        }))

有了这个(为了让事情更简单)我得到了同样的错误

        Orders = _context.Orders.Select(o => new Order
            {
                Id = o.Id,
                Date = o.Date
            })

这些天似乎 LINQ to EF 不是我最好的朋友 :)

于 2013-03-21T16:12:56.087 回答