【问题标题】:Entity Framework 6 - inheritance and navigation properties on base classEntity Framework 6 - 基类的继承和导航属性
【发布时间】:2014-08-20 17:42:18
【问题描述】:

导航属性和继承有问题。

这是我的问题: 我有一个基类Person 和类UserWorker,它们继承自Person。在数据库级别上,我使用单表继承或每层次表 (TPH) 继承。所以有一个带有鉴别器列的表。

UserWorker 都需要有Company 关系,所以我想在Person 类上定义它。

我这样定义我的模型:

[Table("mydb.person")]
public abstract partial class Person
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long ID { get; set; }

    public long? CompanyID { get; set; }
    [ForeignKey("CompanyID")]
    public virtual Company Company { get; set; }
    ...
}

public partial class User : Person
{
    ...
}

public partial class Worker : Person
{
    ....
}

[Table("mydb.company")]
public partial class Company
{
    public Company()
    {
        this.People = new HashSet<Person>();
        this.Users = new HashSet<User>();
        this.Workers = new HashSet<Worker>();
    }

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long ID { get; set; }

    public virtual ICollection<Person> People { get; set; }

    public virtual ICollection<User> Users { get; set; }

    public virtual ICollection<Worker> Workers { get; set; }

    ...
}

现在,当我尝试进行查询以获取用户和相关公司时,例如:

dbSet.Where(u => u.Username == username).Include(x => x.Company).FirstOrDefault();

查询失败,出现以下异常:

“字段列表”中的未知列“Extent1.Company_ID”

如果我检查结果 SQL,它看起来像这样:

SELECT
1 AS `C1`, 
@gp2 AS `C2`, 
`Extent1`.`ID`, 
`Extent1`.`CompanyID`, 
`Extent1`.`Username`,
...
`Extent1`.`Company_ID`
FROM `person` AS `Extent1`
 WHERE `Extent1`.`Discriminator` = @gp1 

它包括额外的Company_ID 列,该列不存在。

我尝试了一些东西,但没有成功:

  • 将列从 CompanyID 重命名为 Company_ID -> 它会在 SQL 中生成 Column_ID1 并引发相同的异常
  • Company 中删除UsersWorkers 关系-> 它抛出一个异常,说它不知道如何映射UserCompany 实体:

无法确定之间关联的主体端 类型“Models.User”和“Models.Company”。这个的主要目的 关联必须使用任一显式配置 关系流式 API 或数据注释。

  • 如果我从 Company 中删除所有 3 个导航属性,则会引发与上述相同的映射异常

我目前没有“干净”的想法。唯一可行的方法是做一些肮脏的修改,定义子类上的所有关系,并在需要用户和工作人员时在基类中进行单独的查询和合并。

你有什么建议吗?

【问题讨论】:

    标签: c# entity-framework inheritance orm entity-framework-6


    【解决方案1】:

    删除 Users 和 Workers 集合属性。

    public virtual ICollection<User> Users { get; set; }
    
    public virtual ICollection<Worker> Workers { get; set; }
    

    由于您的公司导航属性是在 Person 上定义的,因此关联的后退导航属性必须是 Person 的 ICollection。

    People 集合将包含所有相关的工作人员和用户。两个额外的属性 Users 和 Worker 被解释为全新的关系,因为您在 User 或 Worker 上没有相应的属性和外键,所以 EF 虚拟生成了它。

    【讨论】:

    • 我删除了所有显式外键注释和这两个属性,它现在可以工作了。谢谢!只是另一件事......使用这种方法,我如何仅查询用户(没有工作人员)。我知道我可以加载所有人并过滤掉我需要的内容,但这似乎是一种资源浪费。
    • 如果你找到了答案(我知道这个问题很老)但我有完全相同的问题。例如,我只需要我的用户而不是我的工人。解决这个问题的最佳方法是什么?
    【解决方案2】:

    回答评论。只是为了格式化作为第二个答案;-)

    如果您从公司开始,则使用急切加载

    var companies = db.Company.Include(p => p.People);
    

    它总是会得到用户和工人。

    如果您使用从人开始的急切加载。

    var users = db.People.OfType<User>().Include(p => p.Company).ToList();
    var companies = users.Select(p => p.Company).Distinct().ToList();
    

    您公司的人员导航属性只有用户。

    您还可以执行两个单独的语句,数据库上下文的修复将自动填充导航属性。

    var company = db.Company.Where(p => p.ID > 100).ToList();
    var copanyUsers = db.Company.Where(p => p.ID > 100)
                          .SelectMany(p => p.People).OfType<User>().ToList();
    

    【讨论】:

    • 我希望有更好的东西。这与直接查询用户或工作人员基本相同。获取用户和公司是没有意义的,选择公司,通过导航属性再次获取用户。我一直在寻找一种方法来保持双方的关系。但如果不出意外,这将不得不做。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2015-03-29
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多