如何更改作为 one-to-one 关系创建的记录与其自身 Entity Framework 7 (ASP.NET 核心)?

How to change the record created as a one-to-one relation to itself Entity Framework 7 (ASP.NET Core)?

我在项目中使用EntityFramework 7 RC1ASP.NET 5 MVC 6 RC 1

我不工作转换角色(每个角色都有一个parent)。下面是我正在尝试做的一个简化示例。此代码运行成功,不会导致错误。

                var roleDb = roleManager.FindByNameAsync("UserRole").Result;
                var searchRoleDb = roleManager.FindByNameAsync("AdminRole").Result;

                if (roleDb != null)
                {
                    roleDb.DateEdit = DateTime.Now;
                    if (searchRoleDb != null)
                    {
                        roleDb.Parent = searchRoleDb;
                    }
                    context.Entry(roleDb).State = EntityState.Modified;
                    context.SaveChanges();
                }

但是数据库中的ParentId字段仍然为空

public class ApplicationRole : IdentityRole
{
    ...

    public string ParentId { get; set; }
    public virtual ApplicationRole Parent { get; set; }

    ...
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
    {
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Entity<ApplicationRole>()
                .HasOne(c => c.Parent)
                .WithMany()
                .HasForeignKey(c => c.ParentId);
        }
    }

我也成功更改了数据库中的其他数据,以及成功使用数据迁移。

我不能只更改值 ParentId。

这是因为 roleManager 使用的上下文与您用于数据库修改的上下文不同。你应该通过上下文获取角色然后修改它。

            var roleDb = context.Set<IdentityRole>().FirstOrDefault(x => x.Name == "UserRole");
            var searchRoleDb = context.Set<IdentityRole>().FirstOrDefault(x => x.Name == "AdminRole");

            if (roleDb != null)
            {
                roleDb.DateEdit = DateTime.Now;
                context.SaveChanges();
            }

正如我在最初的评论中提到的,您应该在更改角色时使用 asp.net Identity RoleManager class,因为您正在使用它来检索角色作为更改的一部分.您可以使用您的上下文来做到这一点 manually/directly 但这实际上绕过了 asp.net 身份。这是使用 RoleManager 执行更新的原始示例的更新版本:

public async Task UpdateRole()
{
    var roleDb = await roleManager.FindByNameAsync("UserRole");
    var searchRoleDb = await roleManager.FindByNameAsync("AdminRole");

    if (roleDb != null)
    {
        roleDb.DateEdit = DateTime.Now;
        if (searchRoleDb != null)
        {
            roleDb.Parent = searchRoleDb;
        }

        await roleManager.UpdateAsync(roleDb);
    }
}

我还更新了 FindByNameAsync 调用以使用 "await" 而不是“.Result”以避免我提到的潜在死锁问题。但是,要使其正常工作,方法签名必须 return 一个 Task 并包含 "async" 修饰符。如需有关异步内容的帮助,我建议从 here

开始

编辑

根据关于播种的评论,我通常这样播种我的角色:

internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
    protected override void Seed(MyDbContext context)
    {
        SeedRoles(context);
    }

    private static void SeedRoles(MyDbContext context)
    {
        var manager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
        CreateRoleIfNotExists(manager, "Role1");
        CreateRoleIfNotExists(manager, "Role2");
    }

    private static void CreateRoleIfNotExists(RoleManager<IdentityRole> manager, string role)
    {
        if (!manager.RoleExists(role)
        {
            manager.Create(new IdentityRole(role));
        }
    }
}

您需要使用上下文、用户和角色 class 更新代码,因为我刚刚使用了默认值。

无需显式保存更改,因为这是在调用 Seed() returns/completes 时由 DbContext class 完成的。使用这种方法,没有异步调用,因此您无需担心我提到的任何相关问题。