EF4:LINQ 2实体查询在C#中工作,但在VB中不工作

时间:2010-12-11 13:17:18

标签: c# vb.net entity-framework-4

[编辑:我在下面留下了原来的问题,有一些更多的上下文和代码来重现问题。下面的简短版本包含问题的本质]

简短版本:下面的查询抛出System.NotSupportedException:“无法强制转换类型'System.Linq.IOrderedQueryable 1' to type 'System.Linq.IQueryable 1'.LINQ to Entities仅支持转换实体数据模型基元类型。”仅在VB.Net版本中引发异常。当转换为C#时,不会引发任何异常。

   Dim doesThisCrash = From outerOrder In orders
        Where outerOrder.ProductId =
        (From p In products Join o In orders On p.Id Equals o.ProductId
         Order By p.Id
         Select p.Id).FirstOrDefault()
        Select outerOrder
    doesThisCrash.ToList()

因此,为了使其崩溃,似乎我们需要一个子查询,其中原始ObjectSet(订单)与另一个ObjectSet(产品)连接,并且有序。仅使用订单或设置的产品时,不会发生崩溃。当省略Order By时,也没有崩溃。

我倾向于认为这是一个(VB.Net)编译器错误,除非有一些明显的东西我在这里忽略......

现在我的问题仍然存在:

  • 为什么一个看似完全相同的查询在C#中工作但在VB中不起作用?
  • 可以使这个查询在VB.Net中工作吗?

[/编辑]

可选,更长版本(原始问题):

我的域名看起来非常不同,但我将问题转换为更简单的版本,包含以下实体(注意:我实际上使用.edmx设计器定义了这些,因此这是一个简化版本):

    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime DateCreated { get; set; }
    }

    public class Order
    {
        public int Id { get; set; }
        public int CustomerId { get; set; }
        public int ProductId { get; set; }
        public DateTime OrderDate { get; set; }
    }

    public class Customer
    {
        public int Id { get; set; }
    }

我正在尝试在VB.Net中找出一个linq-to-entities查询,它应该在结构上看起来像这样:

    Dim db = New SampleEntities()
    Dim orders As IQueryable(Of Order) = db.Orders
    Dim products As IQueryable(Of Product) = db.Products
    Dim currentDate = DateTime.Now

    Dim qLinq = From outerOrder In orders
                Where outerOrder.OrderDate = currentDate AndAlso
                outerOrder.ProductId =
                    (From p In products Join o In orders On p.Id Equals o.ProductId
                     Where o.OrderDate = outerOrder.OrderDate AndAlso
                            outerOrder.CustomerId = o.CustomerId
                     Order By p.DateCreated
                     Select p.Id).FirstOrDefault()
                Select outerOrder

这会引发System.NotSupportedException:

“无法转换类型'System.Linq.IOrderedQueryable 1' to type 'System.Linq.IQueryable 1'.LINQ to Entities仅支持转换实体数据模型基元类型。”

当省略'Order By'部分时,不会引发任何例外。

我真的没有看到为什么不支持这个查询的原因...所以我决定在C#中尝试相同的事情:

var qLinq = from oOut in orders
            where oOut.OrderDate == currentDate
                  && oOut.ProductId == 
                          (from p in products join o in orders on p.Id equals o.ProductId
                           where oOut.OrderDate == o.OrderDate 
                           && oOut.CustomerId == o.CustomerId
                           orderby p.DateCreated
                           select p.Id).FirstOrDefault()
                    select oOut;

令我惊讶的是,这有效!然后我将C#查询转换为扩展方法语法,然后转回VB,但得到了相同的结果(C#版本工作,VB.Net版本引发了相同的异常)。

所以我猜我的问题是双重的:

  • 为什么一个看似完全相同的查询在C#中工作但在VB中不起作用?
  • 可以使这个查询在VB.Net中工作吗?

供参考,以下是扩展方法语法中的查询:

C#版本:

        var q = orders.Where(outerOrder => 
            outerOrder.OrderDate == currentDate &&
            outerOrder.ProductId == 
            (products
                .Join(orders, 
                    f => f.Id, 
                    o => o.ProductId,
                    (f, o) => new { f, o })
                .Where(t => t.o.OrderDate == outerOrder.OrderDate 
                           && outerOrder.CustomerId == t.o.CustomerId)
                .OrderByDescending(t => t.f.DateCreated)
                .Select(t => t.f.Id))
                .FirstOrDefault());

VB.Net版本:

    Dim q = orders.Where(Function(outerOrder) outerOrder.OrderDate = currentDate AndAlso
                             outerOrder.ProductId = (products.Join(orders,
                               Function(p) p.Id,
                               Function(o) o.ProductId,
                               Function(p, o) New With {.p = p, .o = o}).
                           Where(Function(x) x.o.OrderDate = outerOrder.OrderDate AndAlso
                                     outerOrder.CustomerId = x.o.CustomerId).
                           OrderByDescending(Function(x) x.p.DateCreated).
                           Select(Function(x) x.p.Id).
                           FirstOrDefault()))

4 个答案:

答案 0 :(得分:2)

我认为以下工作,但SQL很难看:

Dim qLinq = From outerOrder In orders
                Where outerOrder.OrderDate = currentDate AndAlso
                outerOrder.ProductId =
                    (From x In (From p In products
                                 Join o In orders On p.Id Equals o.ProductId
                                 Where o.OrderDate = outerOrder.OrderDate AndAlso
                                        outerOrder.CustomerId = o.CustomerId
                                 Select p.Id)
                    Order By x).FirstOrDefault()
                Select outerOrder

答案 1 :(得分:2)

这有用吗?

    Dim a = From p In products Join o In orders On p.Id Equals o.ProductId
            Order By p.DateCreated
            Select New With {.id = p.Id, .OrderDate = o.OrderDate, .CustomerId = o.CustomerId, .DateCreated = p.DateCreated}

    Dim b = From outerOrder In orders _
            Where outerOrder.OrderDate = currentDate _
            AndAlso outerOrder.ProductId = _
            (From x In a Where x.OrderDate = outerOrder.OrderDate _
             AndAlso x.CustomerId = outerOrder.CustomerId _
             Select x.id).FirstOrDefault

答案 2 :(得分:1)

根据我编辑的答案移动Order By p.DateCreated行允许查询在没有任何异常的情况下运行。但是,发出的SQL是不同的,所以我认为你没有得到正确的结果。

Dim qLinq = From outerOrder In orders
            Let id = (From p In products 
                      Order By p.DateCreated
                      Join o In orders On p.Id Equals o.ProductId
                      Where o.OrderDate = outerOrder.OrderDate AndAlso
                            outerOrder.CustomerId = o.CustomerId
                      Select p.Id).FirstOrDefault()
            Where outerOrder.OrderDate = currentDate AndAlso
                  outerOrder.ProductId = id
            Select outerOrder

答案 3 :(得分:-4)

喜 这可以很容易地工作......

而不是IQueryable

Dim orders As IQueryable(Of Order) = db.Orders

使用列表

Dim orders as List(of Order) = db.Orders.ToList()