【问题标题】:DbUpdateException with entities that do not expose foreign key properties带有不公开外键属性的实体的 DbUpdateException
【发布时间】:2012-04-11 20:19:45
【问题描述】:

我有一个包含UserPerson 实体的实体模型,因此每个User 必须与1 个Person 关联,并且每个Person 可以关联零或1 个User

User (0..1) <-------> (1) Person

关联映射流畅。最初我只在Person 一侧声明了它:

private class PersonOrm : EntityTypeConfiguration<Person>
{
    internal PersonOrm()
    {
        ToTable(typeof(Person).Name, DbSchemaName.People);

        // has zero or one User
        HasOptional(p => p.User)
            .WithRequired(d => d.Person)
            .Map(d => d.MapKey("PersonId"))
            .WillCascadeOnDelete(false)
        ;

由于遇到了这个错误,我也将同样的映射添加到User端:

private class UserOrm : EntityTypeConfiguration<User>
{
    internal UserOrm()
    {
        ToTable(typeof(User).Name, DbSchemaName.Identity);

        // has exactly 1 Person
        HasRequired(p => p.Person)
            .WithOptional(d => d.User)
            .Map(d => d.MapKey("PersonId"))
            .WillCascadeOnDelete(false);

在应用程序中有一个场景,可以创建一个新的User 并将其与现有的Person 关联。这是我目前遇到困难的地方。 EF 将User 视为关系的依赖方,并将PersonId (FK, int, not null) 列放在User 表中。我不相信可以在任一实体上使用外键属性来帮助 EF 管理关联(是吗?)。

这是一些尝试处理该场景的失败代码:

// find out if Person already exists
var person = context.People.SingleOrDefault(p => p.Emails.Any(
    e => e.Value.Equals(emailAddress, StringComparison.OrdinalIgnoreCase)));

// find out if User already exists
var user = context.Users.SingleOrDefault(
    u => u.Name.Equals(emailAddress, StringComparison.OrdinalIgnoreCase));

if (user == null)
{
    user = new User
    {
        Name = emailAddress,
        IsRegistered = isRegistered,
        Person = person ?? PersonFactory.Create(emailAddress),
        // ignore the PersonFactory.Create, that part works
    };

    context.Entry(user).State = EntityState.Added;
    context.SaveChanges();
}

person 为空(数据库中尚不存在)时,此代码可以正常工作。但是,当person 不为空(db 中已存在)且user 为空时,代码会尝试创建新的User 并将其与现有的Person 关联。调用SaveChanges() 时,我得到一个DbUpdateException

保存不公开外键的实体时出错 他们关系的属性。 EntityEntries 属性将 返回 null,因为无法将单个实体标识为源 的例外。可以进行保存时的异常处理 通过在实体类型中公开外键属性更容易。看 InnerException 了解详情。

内部异常是:

“User_Person”关联集中的关系位于 “已删除”状态。给定多重约束,对应的 “User_Person_Source”也必须处于“已删除”状态。

这对我来说没有任何意义,因为我没有尝试删除任何内容,并且检查 UserPersonEntityState 表明 User 处于 Added 状态,而Person 处于Unchanged 状态。我已经覆盖了SaveChanges() 来演示:

public override int SaveChanges()
{
    var userChanges = ChangeTracker.Entries<User>().Single();
    var personChanges = ChangeTracker.Entries<Person>().Single();

    if (userChanges.State == EntityState.Deleted)
        throw new InvalidOperationException("wtf?");

    if (personChanges.State == EntityState.Deleted)
        throw new InvalidOperationException("wtf?");

    return base.SaveChanges();
}

当这段代码执行时,InvalidOperationException 都不会被抛出。同样,userChanges 处于 Added 状态,personChanges 处于 Unchanged 状态。

我做错了什么?

【问题讨论】:

    标签: entity-framework entity-framework-4 ef-code-first entity-framework-4.3


    【解决方案1】:

    我现在真的很傻。写了这个仔细的问题后,现在很明显了。

    我正在测试的Person 已经存在,并且已经与另一个User.Name 有一个User 关联。这就是为什么user 将变为空的原因。将Person.User 属性设置为新的User 会导致旧的User 进入Deleted 状态。多哈。

    很抱歉浪费了您的时间。我会留下这个问题,除非它同意删除它会更好。

    【讨论】:

    • 那么你如何解决这个问题?我对具有最后一个帖子关联的主题有类似的问题。如果将最后一个帖子设置为不同的帖子,它会尝试删除上一个帖子! WTF 是这样的吗?
    • 现在是关于将 ORM 从 EF 切换到 NHibernate 的时候了! J/K... 就我而言,这是因为多重性限制。每个用户必须有一个人,但一个人可以有一个空用户。当我将 Person.User 设置为 null 时,EF 首先检查约束以查看是否可以将关系的另一端 (User.Person) 设置为 null。由于不能,EF 将 User 置于已删除状态。如果我将 User.Person 设置为 null,Person 将 不会 进入已删除状态,因为它可以有 0..1 个用户。如果您的限制不同,请发布问题,我会看看。
    【解决方案2】:

    确保您在映射中定义了关系(实体类型配置)

    例如:

    this.HasRequired(t => t.QuoteResponse).WithMany(t => t.QuoteResponseItems).HasForeignKey(d => d.QuoteResponseID);

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-17
      • 1970-01-01
      • 2013-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多