开发者

LINQ query to get all entities with member of specified type

I have following entities and DbContext:

public class Customer
{
    [Key]
    public Guid Guid { get; set; }

    [Required]
    public string FullName { get; set; }

    public virtual ICollection<Order> Orders { get; set; }
}

public abstract class Product
{
    [Key]
    public Guid Guid { get; set; }

    [Required]
    public String Title { get; set; }
}

public class Book : Product
{
    public String Author { get; set; }
    public String Year { get; set; }
}

public class DVD : Product
{
    public String Genre { get; set; }
    public String Country { get; set; }
    public String MPAA { get; set; }
    public String Year { get; set; }
}

public class Order
{
    [Key, Column(Order = 0)]
    public Guid CustomerGuid { get; set; }

    [Key, Column(Order = 1)]
    public Guid ProductGuid { get; set; }

    public Int16 Quantity { get; set; }

    public Decimal Price { get; set; }

    public Decimal Total { get; set; }

    public virtual Customer Customer { get; set; }

    public virtual Product Product { get; set; }
}

public class StoreContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Book> Books { get; set; }
    public DbSet<DVD> DVDs { get; set; }
    public DbSet<Order> Orders { get; set; }

    public StoreContext(string nameOrConnectionString)
        : base(nameOrConnectionString)
    {

    }
}

What is the best way to query all orders where Product is Book? When I use following query I get a bunch of CASE in SQL.

var query = from order in context.Orders
            where order.Product is Book
            select order.Product as Book;

UPDATE Aducci's solution is much better than my, but I still get overloaded SQL in more complex queries.

For example,

var query = context.SaleOffers.Select(x => x.Realty).OfType<Flat>().Select(x => x.Planning.Name);

SQL:

SELECT 
[Extent10].[Name] AS [Name]
FROM    [dbo].[SaleOffers] AS [Extent1]
INNER JOIN [dbo].[Offers] AS [Extent2] ON [Extent1].[Guid] = [Extent2].[Guid]
LEFT OUTER JOIN  (SELECT [UnionAll5].[C1] AS [C1], [UnionAll5].[C2] AS [C2], [UnionAll5].[C3] AS [C3], [UnionAll5].[C4] AS [C4], [UnionAll5].[C5] AS [C5], [UnionAll5].[C6] AS [C6], [UnionAll5].[C7] AS [C7]
    FROM   (SELECT 
        [UnionAll4].[C1] AS [C1], 
        [UnionAll4].[C2] AS [C2], 
        [UnionAll4].[C3] AS [C3], 
        [UnionAll4].[C4] AS [C4], 
        [UnionAll4].[C5] AS [C5], 
        [UnionAll4].[C6] AS [C6], 
        [UnionAll4].[C7] AS [C7]
        FROM  (SELECT 
            [UnionAll3].[C1] AS [C1], 
            [UnionAll3].[C2] AS [C2], 
            [UnionAll3].[C3] AS [C3], 
            [UnionAll3].[C4] AS [C4], 
            [UnionAll3].[C5] AS [C5], 
            [UnionAll3].[C6] AS [C6], 
            [UnionAll3].[C7] AS [C7]
            FROM  (SELECT 
                [UnionAll2].[C1] AS [C1], 
                [UnionAll2].[C2] AS [C2], 
                [UnionAll2].[C3] AS [C3], 
                [UnionAll2].[C4] AS [C4], 
                [UnionAll2].[C5] AS [C5], 
                [UnionAll2].[C6] AS [C6], 
                [UnionAll2].[C7] AS [C7]
                FROM  (SELECT 
                    [UnionAll1].[Guid] AS [C1], 
                    [UnionAll1].[C1] AS [C2], 
                    [UnionAll1].[C2] AS [C3], 
                    [UnionAll1].[C3] AS [C4], 
                    [UnionAll1].[C4] AS [C5], 
                    [UnionAll1].[C5] AS [C6], 
                    [UnionAll1].[C6] AS [C7]
                    FROM  (SELECT 
                        [Extent3].[Guid] AS [Guid], 
                        CAST(NULL AS uniqueidentifier) AS [C1], 
                        cast(0 as bit) AS [C2], 
                        cast(0 as bit) AS [C3], 
                        cast(0 as bit) AS [C4], 
                        cast(0 as bit) AS [C5], 
                        cast(0 as bit) AS [C6]
                        FROM [dbo].[Rooms] AS [Extent3]
                    UNION ALL
                        SELECT 
                        [Extent4].[Guid] AS [Guid], 
                        [Extent4].[PlanningGuid] AS [PlanningGuid], 
                        cast(1 as bit) AS [C1], 
                        cast(0 as bit) AS [C2], 
                        cast(0 as bit) AS [C3], 
                        cast(0 as bit) AS [C4], 
                        cast(0 as bit) AS [C5]
                        FROM [dbo].[Flats] AS [Extent4]) AS [UnionAll1]
                UNION ALL
                    SELECT 
                    [Extent5].[Guid] AS [Guid], 
                    CAST(NULL AS uniqueidentifier) AS [C1], 
                    cast(0 as bit) AS [C2], 
                    cast(1 as bit) AS [C3], 
                    cast(0 as bit) AS [C4], 
                    cast(0 as bit) AS [C5], 
                    cast(0 as bit) AS [C6]
                    FROM [dbo].[CommercialProperties] AS [Extent5]) AS [UnionAll2]
            UNION ALL
                SELECT 
                [Extent6]开发者_运维技巧.[Guid] AS [Guid], 
                CAST(NULL AS uniqueidentifier) AS [C1], 
                cast(0 as bit) AS [C2], 
                cast(0 as bit) AS [C3], 
                cast(1 as bit) AS [C4], 
                cast(0 as bit) AS [C5], 
                cast(0 as bit) AS [C6]
                FROM [dbo].[Houses] AS [Extent6]) AS [UnionAll3]
        UNION ALL
            SELECT 
            [Extent7].[Guid] AS [Guid], 
            CAST(NULL AS uniqueidentifier) AS [C1], 
            cast(0 as bit) AS [C2], 
            cast(0 as bit) AS [C3], 
            cast(0 as bit) AS [C4], 
            cast(1 as bit) AS [C5], 
            cast(0 as bit) AS [C6]
            FROM [dbo].[Garages] AS [Extent7]) AS [UnionAll4]
    UNION ALL
        SELECT 
        [Extent8].[Guid] AS [Guid], 
        CAST(NULL AS uniqueidentifier) AS [C1], 
        cast(0 as bit) AS [C2], 
        cast(0 as bit) AS [C3], 
        cast(0 as bit) AS [C4], 
        cast(0 as bit) AS [C5], 
        cast(1 as bit) AS [C6]
        FROM [dbo].[Lots] AS [Extent8]) AS [UnionAll5]
    INNER JOIN [dbo].[Realties] AS [Extent9] ON [UnionAll5].[C1] = [Extent9].[Guid] ) AS [Join2] ON [Extent2].[RealtyGuid] = [Join2].[C1]
LEFT OUTER JOIN [dbo].[References] AS [Extent10] ON ([Extent10].[Discriminator] = 'FlatPlanning') AND ((CASE WHEN ([Join2].[C1] IS NULL) THEN CAST(NULL AS uniqueidentifier) WHEN ([Join2].[C4] = 1) THEN CAST(NULL AS uniqueidentifier) WHEN ([Join2].[C3] = 1) THEN [Join2].[C2] WHEN ([Join2].[C5] = 1) THEN CAST(NULL AS uniqueidentifier) WHEN ([Join2].[C6] = 1) THEN CAST(NULL AS uniqueidentifier) WHEN ([Join2].[C7] = 1) THEN CAST(NULL AS uniqueidentifier) END) = [Extent10].[Guid])
WHERE CASE WHEN ([Join2].[C1] IS NULL) THEN CAST(NULL AS varchar(1)) WHEN ([Join2].[C4] = 1) THEN '3X0X' WHEN ([Join2].[C3] = 1) THEN '3X1X' WHEN ([Join2].[C5] = 1) THEN '3X2X' WHEN ([Join2].[C6] = 1) THEN '3X3X' WHEN ([Join2].[C7] = 1) THEN '3X4X' ELSE '3X5X' END LIKE '3X1X%'

Thanks in advance!


Not sure if the generated SQL will be any better

var query = context.Orders.Select(order => order.Product).OfType<Book>();


Not a direct answer to your question, but I wonder if you would get better SQL if you added a type discriminator column to Product:

public abstract class Product
{
    [Key]
    public Guid Guid { get; set; }

    [Required]
    public String Title { get; set; }

    public ProductType Type { get; set; }
}

Then you can use that in your query and presumably avoid loading up Products which aren't of the particular type.


I've seen this kind of nastyness in the generated TSQL when using certain inheritance models. I fixed something similar by using EntitySQL instead of LINQ

SELECT VALUE b
FROM OFTYPE(MyNs.Products, MyNs.Book)
AS b

EF short-ciruited the nested subselects that were created using the LINQ.OfType when using the eSQL OFTYPE operator for some reason. (see also http://msdn.microsoft.com/en-us/library/bb399295.aspx#Y639)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜