2

我知道关于这个主题有很多问题,但我还没有找到一个真正解释如何解决我的特定问题的问题。我认为这意味着它可能无法解决(我认为这可能与 EF 的思维方式“倒退”),但我不得不问。

我有一个带有三个(缩写)POCO 的模型,如下所示:

[Table("People")]
public class Person {
    public int PersonID { get; set; }
    [Required]
    public string PersonName { get; set; }
}

public class Location {
    public int LocationID { get; set; }
    public int LocationTypeID { get; set; }
    public virtual LocationType LocationType { get; set; }
}

public class Van : Location {
    public int PartyID { get; set; }
    public virtual Party Party { get; set; }
}

这些由(缩写的)数据库表支持(我们手工编写):

CREATE TABLE People (
    PersonID INTEGER IDENTITY NOT NULL,
    PersonName VARCHAR(255) NOT NULL,
    PRIMARY KEY (PersonID)
)

CREATE TABLE Locations (
    LocationID INTEGER IDENTITY NOT NULL,
    LocationTypeID INTEGER NOT NULL,
    FOREIGN KEY (LocationTypeID) REFERENCES LocationTypes(LocationTypeID)
)

CREATE TABLE Vans (
    LocationID INTEGER NOT NULL,
    PersonID INTEGER NOT NULL,
    PRIMARY KEY (LocationID),
    FOREIGN KEY (LocationID) REFERENCES Locations(LocationID),
    FOREIGN KEY (PersonID) REFERENCES People(PersonID)
)

你大概可以想象它的LocationTypes样子。

Locations是每个类型的表层次结构的根 - 也有检查约束来强制执行这一点。Vans是一种位置,就像其他不相关的东西一样Warehouse

现在,aVan属于 a Person,因为我们向员工发放一辆面包车,他们有责任给它加满燃料,而不是把它撞坏,把它带到客户现场并在他们用完所有供应的东西时订购更多的东西螺钉、钻头和铠装直流电缆。然而,并不是每个Person人都有一辆面包车(其中一些在一辆面包车中成对工作),并且该Person表没有指向的外键Van- 它是相反的。这在某种意义上是一个历史事故,但它非常巧妙地模拟了这种情况,因为虽然 aPerson不必有 a Van,但 aVan肯定必须有一个人。

所以我的问题是:我如何才能Person拥有一个包含它们的导航属性Van

public virtual Van Van { get; set; }

我在数据注释和流畅的 API 方面做了很多工作,我得到的最接近的是OnModelCreating

modelBuilder.Entity<Van>()
    .HasRequired(v => v.Person)
    .WithOptional(p => p.Van);

不幸的是,这试图Van用产生Location对象的代理填充属性。它甚至可能是正确的Location对象(我无法检查),但没有意识到它应该在寻找货车。但是,我确实怀疑它可能会在查找时尝试匹配PersonID-LocationID如果没有 Fluent API 映射,我根本就没有货车,这是我所期望的(所有PersonID值都低于最低LocationID值这对应于货车,所以不可能找到任何东西)。

Person如果对 有一个可以为空的外键,这无疑会很容易Van,但是我们在两个方向上都有外键,如果我们从中取出一个,Van那么我们就不会对 aVan具有 a的绝对必要约束进行建模Person.

所以,我想,Van拥有这种关系,并且Van属性 onPerson是一个反向导航属性,但似乎 EF 并不擅长这种一对一的技巧,即使一端是可选的。有没有办法让它发挥作用,还是我必须接受妥协?

我们通常拒绝因为 Entity Framework 缺少的特性而破坏数据库模型。我真正需要的是一种方法告诉 EF可以通过加入 Vans 上的 Vans.PersonID = Person.PersonID 来填充 on 上的Van属性。Person

4

1 回答 1

4

问题是目前不支持此功能。您提到您没有找到任何问题可以解决您的问题。我想知道您是否发现任何提到 EF 不支持唯一约束/候选键的问题,这对于解决这种类型的一对一关系是绝对必要的。

在数据库中,只有依赖表中的 FK 是唯一的,才能实现一对一的关系。这可以通过两种方式完成:在依赖表中的 FK 列上放置唯一约束(索引)或使用依赖表中的 PK 作为主表的 FK。

EF 执行与数据库相同的参照完整性规则,但在一对一关系和缺乏对唯一约束的支持的情况下,它不支持前一种方式。EF 只能通过将依赖表的 PK 用作对主表的 FK 来对一对一关系进行建模。

您可以投票支持Data UserVoice上的唯一约束/候选键。

如何解决您的特定问题?通过欺骗EF。让 EF 认为您具有一对多关系并PersonIDVan表中放置唯一约束。比这样更新您的 Person :

[Table("People")]
public class Person {
    public int PersonID { get; set; }
    [Required]
    public string PersonName { get; set; }

    public virtual ICollection<Van> Vans { get; set; }

    [NotMapped]
    public Van Van 
    {
        get { return Vans.FirstOrDefault(); }
        set
        {
             Vans.Clear();
             if (value != null)
             {
                 Vans.Add(value);
             }
        }
    }
}

这是一个非常丑陋的解决方法,因为Vans收集仍然是公开的。您可以使用它的可见性,但要确保您了解一些事情:

  • 一旦Vans不公开,您必须将其映射到OnModelCreating该上下文中,并且必须能够看到该属性,或者您必须提供执行该操作的映射配置。检查这个这个以获得更多信息。
  • Vans属性不得破坏代理创建规则以支持延迟加载
  • 急切加载必须使用Vans属性
于 2012-06-09T10:10:12.890 回答