【问题标题】:EF won't update tables based on my modelEF 不会根据我的模型更新表
【发布时间】:2016-08-05 22:09:41
【问题描述】:

起初我创建了一个仅包含基本属性的 Person 模型:

[Table("SGDB_Persons")]
public class Person {
    public int Id { get; set; }

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

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

    [Required]
    public Department Department { get; set; }

    [Required]
    public SourceType SourceType { get; set; }

在我发现我遗漏了一些东西后,我添加了一个新的 PersonData 属性:

[Required]
    public PersonData PersonData { get; set; }

不幸的是,EF 根本不会更新数据库 - 最初包含 Person 类型对象的 PersonData 已更新,因此不再有 Person 属性。另一方面,EF 不会为 PersonData_Id 创建新的列。

此外,ID 列不是自动递增的(所有其他表的 Id 列都可以)。让我感到困惑的是在我的 Person 表中创建的以下约束:

CONSTRAINT [FK_dbo.SGDB_Persons_dbo.SGDB_PersonData_Id] 外键 ([Id]) 引用 [dbo].[SGDB_PersonData] ([Id])

我什么都试过了(至少我是这么认为的)。我手动删除了所有表/整个数据库,重新安装了 EF,执行了手动迁移,但似乎没有任何效果。

我认为正是这个问题导致我无法使用以下代码为我的数据库播种:

protected override void Seed(PersonContext context) {
        base.Seed(context);
        var dep = new DepartmentContext().Departments.First();

        var status = new Status("Test");
        var persondata = new PersonData(status);
        context.Status.Add(status);
        context.PersonData.Add(persondata);
        for (int i = 0; i < 10; i++) {
            var person = new Person {
                Firstname = $"TestPersonFirstname{i}",
                Lastname = $"TestPersonLastname{i}",
                SourceType = COM.SourceType.Manual,
                Department = dep,
                PersonData = persondata
            };
            context.Persons.Add(person);
        }
        context.SaveChanges();
    }

每次执行此代码时,我都会收到异常:

元数据集合中不存在身份为“SGDB.DAL.Contexts.Person_Department”的成员。参数名称:身份。

我不知道这两个问题是否与同一个问题有关,但都需要解决:)

提前致谢!

更新 1

我的解决方案分为几个不同的项目:

BLL、DAL、COM、用户界面

DataContexts 位于 DAL 项目中,Models 位于 COM 项目中。

部门模型:

[Table("SGDB_Departments")]
public class Department {
    public int Id { get; set; }

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

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

    public string Description { get; set; }

    public string FullDepartmentName {
        get {
            return $@"{Division.Abbreviation}\{Abbreviation}";
        }
    }
    [Required]
    public virtual Division Division { get; set; }
}

PersonData 模型:

[Table("SGDB_PersonData")]
public class PersonData {
    public PersonData(Status status) {
        Status = status;
    }

    public int Id { get; set; }
    public DateTime Limit { get; set; }
    public Person Responsible { get; set; }
    [Required]
    public Status Status { get; set; }
}

Person 表(如您所见)有一个 Department_Id 列(EF 自动插入)。

澄清

一个 Person 对象包含一个 PersonData 对象作为此 Person 的附加信息。一个人可能/可能没有责任人(因此 PersonData.Responsible 不是父人的导航属性)。 另外,如果可能的话,我不想在 PersonData 表中有外键。

我发现我必须修改

modelBuilder.Entity<Person>()
       .HasRequired(e => e.PersonData)
       .WithRequiredPrincipal(e => e.Responsible)
       .WillCascadeOnDelete(true);

modelBuilder.Entity<Person>()
       .HasRequired(e => e.PersonData)
       .WithMany()
       .WillCascadeOnDelete(true);

如果它解决了我的问题,我会尝试并报告。

更新 2

元数据集合中不存在身份为“SGDB.DAL.Contexts.Person_Department”的成员。

【问题讨论】:

  • PersonData 是否在同一个项目中?你的上下文是什么样的?
  • 请包括DepartmentPersonData 型号。听起来像是关系配置问题。
  • @SteveGreene - 查看我的问题以获取更新信息。
  • @IvanStoev 我也这么认为。我以相同的方式创建了所有模型——在第一次执行项目后,EF 自己创建了所有东西——甚至外键也是由 EF 创建的(如果不需要,我不想插入额外的 ID 属性)。然后我从 PersonData 中删除了 Person 属性,并在 Person 类中包含了一个 PersonData 属性——此后 EF 从未更改过数据库中的任何内容——即使我在模型中创建了一个新的字符串属性,数据库中也没有任何反应。
  • 我认为问题出在Person.PersonData(必填)、PersonData.Responsible(可选)。在这样的设置中,EF 推断 one-to-one 关系 Person-&gt;PersonDataPersonDataprincipalPerson从属。换句话说,PersonData 拥有Person。这就是为什么自动增量列在PersonData 表中,而Person.Id 很简单int PK 和FK。

标签: c# entity-framework entity-framework-6 datacontext seeding


【解决方案1】:

您的模型定义了PersonPersonData 之间的one-to-one 关系,后者是必需的,前者是可选的。 EF 始终使用one-to-one 关系的必需部分作为主体,将可选部分作为从属。因此它认为PersonaData 是主体,Person - 依赖并反映在数据库表设计中。

你需要相反的,而且双方都是需要。当双方都是必需或可选时,EF 无法自动派生主体/依赖方,并且无法通过数据注释(属性)指定,因此您需要流畅的 API 设置。

覆盖您的 DbContext OnModelCreating 并添加如下内容:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()               
        .HasRequired(e => e.PersonData)
        .WithRequiredPrincipal(e => e.Responsible)
        .WillCascadeOnDelete(true);

    base.OnModelCreating(modelBuilder);
}

它的作用是告诉 EF Person-&gt;PersonData 关系的双方都是必需的,Person 是主体。这应该再次使您的Person.Id 列自动递增,并且应该解决问题的人员数据部分。

我注意到的另一件事是这一行:

var dep = new DepartmentContext().Departments.First();

而同一过程的所有其他部分都使用名为context 的变量。这可能/可能不是问题,只需检查一下。

更新:从更新问题的说明中发现,PersonPersonData 之间存在 两个 关系,因此您需要为每个关系单独配置他们是这样的:

modelBuilder.Entity<Person>()
    .HasRequired(e => e.PersonData)
    .WithRequiredPrincipal()
    .WillCascadeOnDelete(true);

modelBuilder.Entity<PersonData>()
    .HasOptional(e => e.Responsible)
    .WithOptionalDependent() // or WithMany()
    .WillCascadeOnDelete(false);

请注意,没有办法不在PersonData 表中引入额外的 FK 列。它需要表示Responsible 关系,因此您最终会得到一个名为Responsible_Id 的表列。

【讨论】:

  • 正确。这是 EF 在看到 Person Resonsible 时所假设的(没有流畅的配置) - 属性的名称无关紧要,它只查看类型。所以是的,答案不正确,我试图在发布之前澄清这个案例。无论如何,所以您在人和数据之间有 2 个关系,我不确定数据。责任可以设为 1 -> 0..1 在所有情况下,如果您定义关系的双方(双向导航属性)都会有所帮助)。
  • Deal :) 不需要做特殊的事情,一旦你覆盖,它会在需要时被EF调用。
  • 现在我很确定错误是由于不同的上下文。
  • 如果是同一个数据库,那么只需使用context 变量。如果没有,那么您需要先播种(转移)部门。但这是另一个故事,这里无法涵盖。您似乎有两个问题 - 模型设计和种子。我认为我们涵盖了第一个问题,我建议您发布另一个特定于种子问题的问题(可能与此相关)。
猜你喜欢
  • 1970-01-01
  • 2012-10-14
  • 2021-05-26
  • 2016-04-05
  • 2018-06-09
  • 1970-01-01
  • 2017-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多