实体框架6 - 如何添加具有外键IdentityRole的实体

时间:2014-07-17 11:32:18

标签: entity-framework asp.net-identity

我有一个带有映射表的代码优先模型,以便我可以将MenuItem映射到IdentityRole,从而根据登录用户角色分配生成菜单。

public class MenuItem
{
    public int Id { get; set; }
    public string Text { get; set; }
}

public class MenuRoleMap
{
    [Key]
    public int Id { get; set; }

    public virtual MenuItem MenuItem { get; set; }
    public virtual IdentityRole Role { get; set; }
}

IdentityRole和Identity的其余部分通过IdentityDbContext自动连接,我通过我的ApplicationDbContext继承了这个,然后上下文应该是一致的。

public class ApplicationDbContext : IdentityDbContext<User>

所有表看起来都正确,它们有预期的列和外键,这里是MenuRoleMap表

table MenuRoleMap

我有一个有效的现有MenuItem和IdentityRole实例,我用它来尝试向该表添加一个新的实体项

foreach (IdentityRole role in selectedRoles)
{
    MenuRoleMap mrm = new MenuRoleMap();
                mrm.MenuItem = menuItem;
                mrm.Role = role;
                db.MenuRoleMaps.Add(mrm);
}
db.SaveChanges();  ///  <<<=== HERE ERROR BECAUSE THE role IS ALREADY IN DB

会抛出此错误

A first chance exception of type 'System.Data.Entity.Validation.DbEntityValidationException' occurred in EntityFramework.dll
Role: Role SystemsAdministrator already exists.

当然,它确实存在,我知道,它已经存在于数据库中。当然,EF不应该尝试为外键实体添加新的实体项(如果它已经存在)?

它不是针对MenuItem,只针对IdentityRole。

我认为问题是代理创建,因为IdentityRole是一个代理对象,所以我把它关掉了

this.Configuration.ProxyCreationEnabled = false; 

但我仍然得到同样的错误。

我的问题是,如何添加外键为IdentityRole的实体?

谢谢堆叠器。


回答我自己的问题

在下面的那些人的帮助下,我在调查各种解决方案时发现,问题本身不是一个上下文,而是一个对象的有效性。对象看起来正确,我没有意识到的是它不是来自上下文的对象,它是一个传真。通过尝试将此传真添加到模型中,上下文非常正确地说它已经存在,您无法再次添加它。通过尝试覆盖项目的状态,我创建了一种不同的错误。

解决方案只是从上下文重新加载对象,然后将其添加到父项目中,如此

foreach (IdentityRole role in selectedRoles)
{
    // Here I'm getting the role from the context using the ID I have from the facsimile
    IdentityRole roleToUse = db.Roles.Where(x => x.Id == role.Id).FirstOrDefault();
    // carry on as normal
    MenuRoleMap mrm = new MenuRoleMap();
    mrm.MenuItem = menuItem;
    mrm.Role = roleToUse;  // note I'm using the retrieved 'roleToUse'
    db.MenuRoleMaps.Add(mrm);
}
db.SaveChanges();

嘿presto一切正常。

3 个答案:

答案 0 :(得分:2)

您似乎已经从另一个上下文中检索了该实体,然后将其分配给一个实体,然后将该实体添加到另一个上下文中。然后它会尝试插入Role实体。

您是否从另一个将上下文的生命周期限定为该方法的方法返回Role?

您可能会发现以下链接在更新对象状态方面很有用: Entity states and SaveChanges

答案 1 :(得分:0)

问题在于,当您使用db.Set<MyEntity>.Add时,您将标记附加到已添加为添加的实体的所有实体。您必须明确标记它们不变:

foreach (IdentityRole role in selectedRoles)
{
    MenuRoleMap mrm = new MenuRoleMap();
                mrm.MenuItem = menuItem;
                mrm.Role = role;
                db.MenuRoleMaps.Add(mrm);
                db.Entry(role).State=EntityState.Unchanged;
}
db.SaveChanges();  

答案 2 :(得分:0)

我的答案是你不能或至少不应该。

身份验证(角色)和业务(菜单)是应用程序的不同问题。

对我来说,你必须引入ApplicationDb,它是你需要的IdentityDb的一部分,并组织同步。

为了说明我的说法:使用Google或LiveID作为身份验证提供程序进行映像:您能想象从ApplicationDd到Google或Microsoft Dbs的导航属性吗?

显然不是。

因此,创建一个复制身份验证数据库角色的AppRole,并使用应用程序数据库中的此表来构建菜单。

伪代码中,这看起来像:

List<Int32> l = IdentityContext.GetRolesForUser(currentUserId);
foreach (AppRole role in AppContext.Roles.Where(r => l.Contains(r.Id)))
{
    MenuRoleMap mrm = new MenuRoleMap();
            mrm.MenuItem = menuItem;
            mrm.Role = role;
            appContext.MenuRoleMaps.Add(mrm);
}
appContext.SaveChanges();

另一种解决方案是对Application和Identity使用相同的上下文。

上下文的继承似乎很好,但我从未测试过它。