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

dan*_*wig 8 entity-framework entity-framework-4 ef-code-first entity-framework-4.3

我的实体模型UserPerson实体,使得每个User必须正好1相关联Person,并且每个Person可以被关联0或1 User.

User (0..1) <-------> (1) Person
Run Code Online (Sandbox Code Playgroud)

该关联可以流畅地映射.最初我只在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)
        ;
Run Code Online (Sandbox Code Playgroud)

由于我遇到了这个错误,我还在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);
Run Code Online (Sandbox Code Playgroud)

在应用程序中有一个场景,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();
}
Run Code Online (Sandbox Code Playgroud)

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

保存不公开其关系的外键属性的实体时发生错误.EntityEntries属性将返回null,因为无法将单个实体标识为异常的来源.通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常.有关详细信息,请参阅InnerException.

内在的例外是:

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

这对我没有任何意义,因为我不是要删除任何东西,并检查EntityState两者UserPerson显示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();
}
Run Code Online (Sandbox Code Playgroud)

执行此代码时,不会InvalidOperationException抛出任何代码.再次,userChanges处于Added州,并personChanges处于Unchanged州.

我究竟做错了什么?

dan*_*wig 7

我现在觉得很蠢.写完这个小心的问题之后,现在很明显了.

Person我与测试已经存在,并且已经具备了User与不同的关联User.Name.这就是为什么user即将出现的原因.将Person.User属性设置为new User会导致旧UserDeleted状态.卫生署.

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

  • 将ORM从EF切换到NHibernate是*大约*时间!J/K ......就我而言,这是因为多重性限制.每个用户**必须有一个人,但一个人可以拥有一个空用户.当我将Person.User设置为null时,EF首先检查约束以查看关系的另一侧(User.Person)是否可以设置为null.由于它不能,EF将用户置于删除状态.如果我将User.Person设置为null,则Person****不会被置于已删除状态,因为它可以具有0..1 User.如果您的约束不同,请提出一个问题,我会看看. (2认同)