使用EF和Code First更新具有递归关联的记录时出错

时间:2012-11-09 17:28:36

标签: c# entity-framework ef-code-first code-first poco

我想更新记录类型图,它修改父对象和内部也有子对象。当孩子的EF UPDATE注册表将子项置于NULL时,子项有助于识别要更新的记录,通常会出现此问题。

我的域名类是:

这个类帮助我改变状态,添加 - 自定义 - 删除,这是父亲作为所有孩子的图表。使其有效的功能。

public interface IObjectWthState
{
    State State { get; set; }
}
public enum State 
{
    Added,
    Unchanged,
    Modified,
    Deleted
}

这是一个用户类:

 public abstract class User : IObjectWthState 
 {
    public int Id { get; set; }
    public String Name { get; set; }
    public String LastName { get; set; }        
    [Timestamp]
    public byte[] RowVersion { get; set; }
    [NotMapped]
    public State State { get; set; }
}

以下是从用户继承的两个类:

public class AdminUser:User
{
    public ICollection<BasicUser> UsersList { get; set; }
    public String Email { get; set; }
}

public class BasicUser: User
{
    public String Position { get; set; }
    public String Department { get; set; }
}

正如所见,AdminUser BasicUser有一个列表。

模型是按照需要生成的,需要检测外键并添加。这是DB的图片:

Model generated with migration

这是添加或更新infromación的功能:

public virtual void AddUpdateGraph(T entity)
{

    if (((IObjectWthState)entity).State == State.Added)
    {
        DbEntityEntry dbEntityEntry = dbContext.Entry(entity);
        dbEntityEntry.State = EntityState.Added;
    }
    else
    {
        dbSet.Add(entity);
        dbContext.ApplyStateChanges();
    }
} 

处理的函数去调整内部节点的状态:

public static void ApplyStateChanges(this DbContext context)
{
    foreach (var entry in context.ChangeTracker.Entries<IObjectWthState>())
    {
        IObjectWthState stateInfo = entry.Entity;
        entry.State = StateHelpers.ConvertState(stateInfo.State);
    }
}  

将状态返回给EF的函数:

public static EntityState ConvertState(State state)
{
    switch (state)
    {
        case State.Added:
            return EntityState.Added;
        case State.Modified:
            return EntityState.Modified;
        case State.Deleted:
            return EntityState.Deleted;
        default:
            return EntityState.Unchanged;
    }
}

如果要添加一个新的AdminUser及其列表BasicUser一切正常,没有问题,当您想要修改EF BasicUser AdminUser并为BasicUser生成更新但添加了外键为null的条件时,会出现问题

在这里,您可以看到生成的两个更新

ADMINUSER:

 exec sp_executesql N'update [dbo].[User]
set [Name] = @0, [LastName] = @1, [Email] = @2
where (([Id] = @3) and ([RowVersion] = @4))
select [RowVersion]
from [dbo].[User]
where @@ROWCOUNT > 0 and [Id] = @3',N'@0 nvarchar(max) ,@1 nvarchar(max) ,@2 nvarchar(max) ,@3 int,@4 binary(8)',@0=N'Beto',@1=N'Guerere',@2=N'beto@gmail.com',@3=3,@4=0x0000000000000801

BasicUser:

exec sp_executesql N'update [dbo].[User]
set [Name] = @0, [LastName] = @1, [Position] = @2, [Department] = @3, [AdminUser_Id] = @4
where ((([Id] = @5) and ([RowVersion] = @6)) and [AdminUser_Id] is null)
select [RowVersion]
from [dbo].[User]
where @@ROWCOUNT > 0 and [Id] = @5',N'@0 nvarchar(max) ,@1 nvarchar(max) ,@2 nvarchar(max) ,@3 nvarchar(max) ,@4 int,@5 int,@6 binary(8)',@0=N'Viomar',@1=N'Guerere',@2=N'Supervisora',@3=N'Ventas',@4=3,@5=4,@6=0x0000000000000802

正如您在EF生成的SQL命令中看到的那样,添加了BasicUser的更新,记录的条件具有AdminUser_Id的Null值。我不明白这个的原因。该字段不能为空,因为该用户已分配给主管。

我希望我解释一下。

非常感谢您给我的任何帮助。

2 个答案:

答案 0 :(得分:0)

您是否正在检索已经分配了adminuser的基本用户?在我看来,从您将其检索到您进行更新时,adminuserid不会被保留。我经常看到这种情况,例如在MVC应用程序中,您不希望显示该外键,因此您不会将其包含在视图中。但是当您更新并将对象发送到控制器时,它会创建一个具有空FK的新用户实例。当您使用该FK作为并发检查字段时,在从控制器移动到视图到控制器时保留该值非常重要。如果是这种情况,幸运的是我已经有关于这个问题的博客文章,所以我不想在这里重新解释:http://thedatafarm.com/blog/data-access/round-tripping-a-timestamp-field-with-ef4-1-code-first-and-mvc-3/

如果确实如此,请告诉我。如果是这样,希望它能解决您的问题。如果没有,我可能会有更多问题! :)

(p.s。其余代码看起来非常熟悉!:)来自我的&amp;罗恩关于DbContext的书? :))

答案 1 :(得分:0)

向我回答我的问题:EF认为数据库中的FK无论如何你必须在模型中与她一起工作,如果你断开工作,你必须确保你的信息存储。

首先要做的是在BasicUser类中添加一个int类型的变量,我们称之为AdminUser_Id:

public class BasicUser: User
    {       
        public String Position { get; set; }
        public String Department { get; set; }
        public int AdminUser_Id { get; set; }
    }

然后我们必须告诉EF,添加的变量是引用AdminUser AdminUser的FK,并且可以有许多BasicUser。

为了做到这一点,我们去了我们声明上下文的地方,DataLayer(在我的情况下),在创建模型的cuncion中我们添加了以下注释:

modelBuilder.Entity<AdminUser>()HasMany(A => A.UsersList ).WithRequired().HasForeignKey(B => B.AdminUser_Id );

We update the model and with these adjustments the mistake is eliminated completely at the moment of realizing the update of the record.

有关FK和EF的更多信息,建议您阅读本文:

  1. Making Do with Absent Foreign Keys
  2. Code First Relationships Fluent API
  3. 非常感谢所有人特别向Julie Lerman求助,如果没有它,我会花费更多资金来解决这个问题。

    我希望我已经解释过了。