在foreach循环实体框架中附加的ObjectStateManager错误

时间:2013-04-27 20:46:14

标签: c# entity-framework

所以我有一个(新)用户表和一个组表。我要做的是将用户添加到组中。

我以为我会做的是: -

using(context = new MyEntity())
{
    foreach(csvUser from csvSource)
    {
        User oUser = new User();
        oUser.Firstname = csvUser.Firstname;

        Group oGroup = new Group();
        // Set the primary key for attach
        oGroup.ID = csvUser.GroupID;
        context.Group.Attach(oGroup);

        oUser.Groups.Add(oGroup);
        context.Users.Add(oUser);
    }   

    context.saveChnages();
}

因此,通过循环遍历所有新用户,从CSV文件中获取他们的组ID(组已经存在于db中)。所以我会附加到该组,然后添加该组。

但是我遇到了一个错误,因为只要已经附加了组ID的用户尝试附加它就会出现问题。

  

具有与所提供对象的键匹配的键的对象可以   在ObjectStateManager中找不到。验证密钥值   提供的对象匹配对象的键值   必须应用更改。

我可以理解,它试图重新附加已经附加到内存中的对象。但有没有办法解决这个问题呢?我需要做的就是将新用户从数据库附加到预先存在的组。

3 个答案:

答案 0 :(得分:1)

当它尝试更新(先前)分离的实体时,该错误通常与某种形式或形状的ApplyCurrentValues相关联。

目前还不完全清楚为什么会发生这种情况 - 但也许你还有其他事情要发生 - 或者你只是'混淆'EF与重新连接同一组。

我认为最简单的方法是使用Find - 并避免使用Attach

context.Group.Find(csvUser.GroupID);  

如果有缓存,则从缓存加载 - 如果需要,从Db加载。

  

如果上下文中存在具有给定主键值的实体,   然后立即返回,而不向商店提出请求。   否则,向商店请求具有给定的实体   主键值和此实体(如果找到)附加到   上下文并返回。如果在上下文中没有找到实体或者   store,然后返回null

那应该为你解决问题。

你也可以关闭我认为的Db值(但我现在无法检查)。

答案 1 :(得分:0)

您似乎在用户和群组之间存在多对多关系。如果是这种情况并且您使用的是Code-First,那么您可以像这样定义模型......

public class User
{
    public int Id { get; set; }
    public string Firstname { get; set; }
    // Other User properties...
    public virtual ICollection<UserGroup> UserGroups { get; set; }
}

public class Group
{
    public int Id { get; set; }
    // Other Group properties...
    public virtual ICollection<UserGroup> UserGroups { get; set; }
}

public class UserGroup
{
    public int UserId { get; set; }
    public User User { get; set; }

    public int GroupId { get; set; }
    public Group Group { get; set; }
}

接下来,配置多对多关系......

public class UserGroupsConfiguration : EntityTypeConfiguration<UserGroup>
{
    public UserGroupsConfiguration()
    {
        // Define a composite key
        HasKey(a => new { a.UserId, a.GroupId });

        // User has many Groups
        HasRequired(a => a.User)
            .WithMany(s => s.UserGroups)
            .HasForeignKey(a => a.UserId)
            .WillCascadeOnDelete(false);

        // Group has many Users
        HasRequired(a => a.Group)
            .WithMany(p => p.UserGroups)
            .HasForeignKey(a => a.GroupId)
            .WillCascadeOnDelete(false);
    }
}

在DbContext类中添加配置...

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new UserGroupsConfiguration());
    ...
}

现在你的任务更简单......

foreach (var csvUser in csvSource)
{
    User oUser = new User();
    oUser.Firstname = csvUser.Firstname;

    // Find Group
    var group = context.Groups.Find(csvUser.GroupID);

    if(group == null)
    {
       // TODO: Handle case that group is null
    }
    else
    {
        // Group found, assign it to the new user
        oUser.UserGroups.Add(new UserGroup { Group = group });
        context.Users.Add(oUser);
    }
}
context.SaveChanges();

答案 2 :(得分:0)

您似乎正在为正在迭代的每个Group添加新的User,请尝试以下操作:

using(context = new MyEntity())
{
    // fetch Groups and add them first
    foreach(groupId in csvSource.Select(x => x.GroupID).Distinct())
    {
         context.Groups.Add(new Group { ID = groupId });
    }

    // add users
    foreach(csvUser from csvSource)
    {
        User oUser = new User();
        oUser.Firstname = csvUser.Firstname;

        var existingGroup = context.Groups.Single(x => x.Id == csvUser.GroupID);
        oUser.Groups.Add(existingGroup);

        context.Users.Add(oUser);
    }   

    context.saveChnages();
}