DbUpdateException,其实体不公开外键属性

时间:2012-04-11 20:19:45

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

我有一个包含UserPerson个实体的实体模型,因此每个User必须与1个Person相关联,并且每个Person都可以与0或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为空(db中尚不存在)时,此代码正常工作。但是,如果person不为null(已存在于db中)且user为null,则代码会尝试创建新的User并将其与现有Person关联。在调用SaveChanges()时,我得到DbUpdateException

  

保存不公开外键的实体时发生错误   他们关系的属性。 EntityEntries属性将   返回null,因为无法将单个实体标识为源   例外。可以在保存时处理异常   通过在实体类型中公开外键属性更容易。看到   InnerException以获取详细信息。

内部例外是:

  

来自'User_Person'AssociationSet的关系在   '删除'状态。给定多重约束,相应的   'User_Person_Source'也必须处于'已删除'状态。

这对我没有任何意义,因为我不是要删除任何内容,而检查EntityStateUser的{​​{1}}会显示Person处于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状态。

我做错了什么?

2 个答案:

答案 0 :(得分:6)

我现在觉得自己很蠢。在写完这个仔细的问题后,现在很明显了。

我正在测试的Person已经存在,并且已经与其他User建立了User.Name关联。这就是user即将出现的原因。将Person.User属性设置为新User会导致旧User处于Deleted状态。卫生署。

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

答案 1 :(得分:0)

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

例如:

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