EF Linq to 实体查询为 TPC CTP5 代码优先实体生成 UNION

发布于 2024-10-15 01:59:48 字数 1929 浏览 1 评论 0原文

我有以下实体:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class HappyUser : User
{
    public bool IsHappy { get; set; }
}

我正在使用每个具体类型表 (TPC) 配置实体,以便生成用户表和 HappyUser 表。我希望 HappyUser 表包含 User 类的属性,并且我不希望两个表之间有任何关系。

我按如下方式配置实体:

public class UserTest : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<HappyUser> HappyUsers { get; set; }


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<HappyUser>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("HappyUser");
        });
    }
}

表已正确生成,但是当我查询表时,EF 在 User 和 HappyUser 表上生成 UNION。查询如下:

        UserTest db = new UserTest();

        var users = from u in db.Users
                    select u;

        var happyUsers = from u in db.Users.OfType<HappyUser>()
                         select u;

Users 的 SQL 生成 UNION。这不是我所期望或想要的。我想简单地从用户表中检索行。

SELECT 
CASE WHEN ([UnionAll1].[C2] = 1) THEN '0X' ELSE '0X0X' END AS [C1], 
[UnionAll1].[Id] AS [C2], 
[UnionAll1].[Name] AS [C3], 
CASE WHEN ([UnionAll1].[C2] = 1) THEN CAST(NULL AS bit) ELSE [UnionAll1].[C1] END AS [C4]
FROM  (SELECT 
 [Extent1].[Id] AS [Id], 
 [Extent1].[Name] AS [Name], 
 CAST(NULL AS bit) AS [C1], 
 cast(1 as bit) AS [C2]
 FROM [dbo].[User] AS [Extent1]
UNION ALL
 SELECT 
 [Extent2].[Id] AS [Id], 
 [Extent2].[Name] AS [Name], 
 [Extent2].[IsHappy] AS [IsHappy], 
 cast(0 as bit) AS [C1]
 FROM [dbo].[HappyUser] AS [Extent2]) AS [UnionAll1]

HappyUsers 的 SQL 按预期工作。

SELECT 
'0X0X' AS [C1], 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
[Extent1].[IsHappy] AS [IsHappy]
FROM [dbo].[HappyUser] AS [Extent1]

有什么想法我做错了吗?或者这是 EF 的缺陷?

I have the following entities:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class HappyUser : User
{
    public bool IsHappy { get; set; }
}

I am configuring the entities using Table Per Concrete Type (TPC) in order to generate a User table and a HappyUser table. I want the HappyUser table to include the properties of the User class, and I don't want any relationship between the two tables.

I configure the entities as follows:

public class UserTest : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<HappyUser> HappyUsers { get; set; }


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<HappyUser>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("HappyUser");
        });
    }
}

The tables are generated correctly, but when I query the tables, EF generates a UNION on the User and HappyUser table. The query is as follows:

        UserTest db = new UserTest();

        var users = from u in db.Users
                    select u;

        var happyUsers = from u in db.Users.OfType<HappyUser>()
                         select u;

The SQL for the Users generates a UNION. This is not what I expect or want. I'd like to simply retrieve the rows from the Users table.

SELECT 
CASE WHEN ([UnionAll1].[C2] = 1) THEN '0X' ELSE '0X0X' END AS [C1], 
[UnionAll1].[Id] AS [C2], 
[UnionAll1].[Name] AS [C3], 
CASE WHEN ([UnionAll1].[C2] = 1) THEN CAST(NULL AS bit) ELSE [UnionAll1].[C1] END AS [C4]
FROM  (SELECT 
 [Extent1].[Id] AS [Id], 
 [Extent1].[Name] AS [Name], 
 CAST(NULL AS bit) AS [C1], 
 cast(1 as bit) AS [C2]
 FROM [dbo].[User] AS [Extent1]
UNION ALL
 SELECT 
 [Extent2].[Id] AS [Id], 
 [Extent2].[Name] AS [Name], 
 [Extent2].[IsHappy] AS [IsHappy], 
 cast(0 as bit) AS [C1]
 FROM [dbo].[HappyUser] AS [Extent2]) AS [UnionAll1]

The SQL for the HappyUsers works as expected.

SELECT 
'0X0X' AS [C1], 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
[Extent1].[IsHappy] AS [IsHappy]
FROM [dbo].[HappyUser] AS [Extent1]

Any ideas what I am doing wrong? Or is this a defect in EF?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

允世 2024-10-22 01:59:48

HappyUsers用户。因此,db.Users 应该返回两者。 EF 在这里是正确的。

但是,EF 确实有一个限制:(无论如何,在 L2E 中)无法返回一种类型的结果。由于里氏替换原则,您通常不想这样做。但如果您这样做,您可以在 ESQL 中完成此操作,并且有(有些乏味的)L2E 解决方法

底线:如果您发现自己想这样做,请重新考虑您的设计。在这种情况下,继承可能不是正确的关系。

HappyUsers are Users. Hence, db.Users should return both. The EF is correct, here.

However, the EF does have a limitation: There is no way to (in L2E, anyway) return results of only one type. Due to the Liskov Substitution Principal, you don't generally want to do this. But if you do, you can do it in ESQL, and there are (somewhat tedious) workarounds for L2E.

Bottom line: If you find yourself wanting to do this, rethink your design. Inheritance is probably not the right relationship in this case.

落在眉间の轻吻 2024-10-22 01:59:48

@Craig Stuntz 是对的,因为两种类型都是相关的,EF 将保持这种关系完整。

如果您想解耦两种 User 类型,那么您可以使用抽象基类。

public abstract class AbstractUser
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class User : AbstractUser
{
}

public class HappyUser : AbstractUser
{
    public bool IsHappy { get; set; }
}

EF 现在会将这两个实体视为单独的实体,无需再调用 MapInheritedProperties()

您的选择语句将如下所示:

var users = db.Users.Where(...);
var happyUsers = db.HappyUsers.Where(...);

@Craig Stuntz is right, since both types are related, EF will keep that relation intact.

If you want to decouple both User types, then you could use an abstract base class.

public abstract class AbstractUser
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class User : AbstractUser
{
}

public class HappyUser : AbstractUser
{
    public bool IsHappy { get; set; }
}

EF will now treat both entities as seperate and there's no need to call MapInheritedProperties() anymore.

Your select statements would then look like:

var users = db.Users.Where(...);
var happyUsers = db.HappyUsers.Where(...);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文