EntityType 'IdentityUserRole' не имеет ключевой ошибки

Сначала работаем с кодом фреймворка сущности.

Попытка использовать идентификатор IdentityRole в качестве внешнего ключа для настраиваемого класса.

связь правильная, но когда я пытаюсь добавить данные в свою настраиваемую таблицу, я получаю эту ошибку.

EntityType 'IdentityUserRole' не имеет определенного ключа. Определите ключ для этого EntityType. IdentityUserRoles: EntityType: EntitySet «IdentityUserRoles» основан на типе «IdentityUserRole», для которого не определены ключи.

Код:

Пользовательский стол / класс

public class UserRoleAccess
{
    [Key]
    public long UserRoleAccessId { get; set; }

    public string UserRoleId { get; set; }

    public virtual ApplicationRole ApplicationRole { get; set; }

    public bool IsActive { get; set; }

    public string Name { get; set; }

    public int UserRoleAccessType { get; set; }
}

IdentityRole:

public class ApplicationRole : IdentityRole
{
    public virtual ICollection<UserRoleAccess> UserRoleAccess { get; set; }
}

Привязки:

    protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();


        modelBuilder.Entity<IdentityUser>().ToTable("Users").Property(p => p.Id).HasColumnName("UserId");
        modelBuilder.Entity<ApplicationUser>().ToTable("Users").Property(p => p.Id).HasColumnName("UserId");
        modelBuilder.Entity<IdentityUserRole>().ToTable("UserRoles");
        modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogins");
        modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaims");
        modelBuilder.Entity<IdentityRole>().ToTable("Roles");
        modelBuilder.Entity<ApplicationRole>().ToTable("Roles");
        modelBuilder.Entity<UserRoleAccess>().ToTable("UserRoleAccess");

        modelBuilder.Entity<ApplicationUser>().HasRequired(p => p.Person)
            .WithMany(b => b.Users);

        modelBuilder.Entity<ApplicationUser>().HasRequired(p => p.Person)
            .WithMany(b => b.Users)
            .HasForeignKey(p => p.PersonId);

        modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
        modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
        modelBuilder.Entity<ApplicationRole>().HasKey<string>(r => r.Id);
        modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });


    }

Связь в БД (правильная):

введите здесь описание изображения

Пытался создать свойство id и установить атрибут [Key], но это не сработало:

public class ApplicationRole : IdentityRole
{
    [Key]
    public string Id { get; set; }

    public virtual ICollection<UserRoleAccess> UserRoleAccess { get; set; }
}

Запустите этот код при добавлении данных:

    public static void CreateUserRoleAccess(string name, int type, string id)
    {

        using (var context = new UserRoleAccessContext())
        {
            var userRoleAccess = new UserRoleAccess();

            userRoleAccess.ApplicationRole = new ApplicationRole { Id = id };

            userRoleAccess.UserRoleId = id;
            userRoleAccess.IsActive = true;

            userRoleAccess.UserRoleAccessType = type;

            userRoleAccess.Name = name;

            context.UserRoleAccess.Add(userRoleAccess);

            context.SaveChanges();
        }
    }

Что мне не хватает?

РЕДАКТИРОВАТЬ:

Класс IdentityRole:

IdentityRole


person ThunD3eR    schedule 11.10.2016    source источник
comment
Мы не используем ключевое слово new перед ObjectType, не так ли? общедоступная новая строка Id {получить; установленный; }   -  person Waleed    schedule 11.10.2016
comment
@Waleed что-то resharper добавил. как ваш комментарий помогает? С ключевым словом new перед свойством или без него. Он по-прежнему не работает.   -  person ThunD3eR    schedule 11.10.2016
comment
Можете ли вы опубликовать класс IdentityRole? У вас есть сопоставление с ключом IdentityRole в построителе модели.   -  person matthijsb    schedule 11.10.2016
comment
@matthijsb - это встроенный класс Ef: msdn .microsoft.com / en-us / library / dn613249 (v = vs.108) .aspx.   -  person ThunD3eR    schedule 11.10.2016
comment
В сообщении об ошибке указано, что ошибка связана с сущностью IdentityUserRole, а не с IdentityRole. Ваши сопоставления не определяют отношения между этой сущностью и другими сущностями, поэтому EF, вероятно, неявно создает отношения для этой сущности. Создается ли PK в БД в таблице UserRoles? Не могли бы вы показать код сущности IdentityUserRole?   -  person Diana    schedule 12.10.2016
comment
@Diana msdn.microsoft.com/en-us /library/dn613252(v=vs.108).aspx   -  person ThunD3eR    schedule 12.10.2016
comment
Возможно, физический ключ не сгенерирован как ключ, который вы указали в сопоставлениях. Можете ли вы посмотреть на ключи (первичные или иные), существующие в физической таблице UserRoles?   -  person Diana    schedule 12.10.2016


Ответы (1)


Итак, вы используете MVC 5 с ASP.NET Identity 2.0. Думаю, в вашем проекте есть пакет Nuget для Microsoft.AspNet.Identity.EntityFramework, и в вашей базе данных созданы нужные таблицы.

Возможно, ваша проблема в том, что вы переопределяете сопоставления для стандартных таблиц идентификации. Вам действительно нужно явно отображать имена таблиц и ключи? Я бы попытался сохранить исходные сопоставления и добавить явные сопоставления только для ваших настраиваемых сущностей и полей. Я думаю, вам не нужны сопоставления ToTable, поскольку базовые сущности имеют сопоставление где-то в своем коде, а ваши сущности расширяют их.

Пара вопросов:

  • Что означает UserRoleAccess.UserRoleId? Это идентификатор пользователя или идентификатор роли? Если предполагается, что он содержит идентификатор UserRole, это составной ключ, а не отдельное значение, поэтому маловероятно, что свойство String подойдет. Возможно, тебе стоит его переименовать.

  • Что именно вы хотите сделать в методе CreateUserRoleAccess: добавить новый UserRoleAccess для роли и пользователя, которые уже существуют и уже связаны (то есть отношение UserRole уже существует)? Или добавить новые UserRole и UserRoleAccess для пользователя и роли, которые уже существуют, но не связаны? Я предполагаю, что вы не хотите создавать роль или пользователя на этом этапе, они уже существуют, я ошибаюсь?

  • Во всяком случае, в вашем методе CreateUserRoleAccess в этой строке:

    userRoleAccess.ApplicationRole = new ApplicationRole { Id = id };
    

    Если вы не создаете новую роль в этот же момент, вам следует выполнить Find или другой запрос Linq и получить существующую роль из контекста. Возможно, просто изменив это, ваш код заработает:

    //userRoleAccess.ApplicationRole = new ApplicationRole { Id = id };
    userRoleAccess.ApplicationRole = context.Roles.Find(id) as ApplicationRole;
    

Я создал проект MVC и попробовал ваш код с несколькими изменениями:

Я не включил никаких явных привязок или сопоставлений EF, просто чтобы попробовать с исходной моделью Identity и посмотреть, работает ли она.

Я расширил Identity DbContext, просто добавив DbSet для UserRoleAccess:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public virtual IDbSet<UserRoleAccess> UserRoleAccesses { get; set; }

    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}  

После добавления ваших настраиваемых сущностей ApplicationRole и UserRoleAccess, генерации миграции и обновления базы данных я получил ту же таблицу UserRoleAccess, что и вы, но также новый столбец Discriminator в таблице Roles:

введите описание изображения здесь

Это означает, что EF использует TPH (таблица на иерархию) при расширении сущности IdentityRole с помощью сущности ApplicationRole. Они делят стол. Это неявно, нет необходимости писать привязки (на самом деле, ненужные явные привязки имеют тенденцию мешать).

Итак, если в вашей таблице ролей отсутствует это поле, это может быть вероятной причиной вашей ошибки.

Я запустил сеть MVC и создал нового пользователя со ссылкой «Зарегистрироваться» в заголовке страницы. Я запустил следующий код (добавив его в контроллер индекса), и он сработал:

    public void CreateUserRoleAccess(string name, int type, string id)
    {
        //Insert role (run only once)
        using (var context = new ApplicationDbContext())
        {
            var role = new ApplicationRole { Id = id, Name = "Role1" };
            var user = context.Users.FirstOrDefault<ApplicationUser>();
            //user.Roles.Add(role);
            role.Users.Add(new IdentityUserRole { RoleId = role.Id, UserId = user.Id });

            context.Roles.Add(role);
            context.SaveChanges();
        }

        using (var context = new ApplicationDbContext())
        {
            var userRoleAccess = new UserRoleAccess();

            //userRoleAccess.ApplicationRole = new ApplicationRole { Id = id };
            userRoleAccess.ApplicationRole = context.Roles.Find(id) as ApplicationRole;

            //Is this a userId or a roleId? Does not seem OK to assign id param here
            userRoleAccess.UserRoleId = id;
            userRoleAccess.IsActive = true;

            userRoleAccess.UserRoleAccessType = type;

            userRoleAccess.Name = name;

            context.UserRoleAccesses.Add(userRoleAccess);

            context.SaveChanges();
        }
    }

Обратите внимание, что сначала я создаю новую роль с другой областью контекста, чтобы убедиться, что роль существует, когда я перейду к интересному коду. Я использую эту роль позже, извлекая ее из контекста.

Надеюсь это поможет. Если хотите, я могу отправить вам полный код решения.

РЕДАКТИРОВАТЬ - Таблица UserRoleAccess:

Таблица UserRoleAccess

        CreateTable(
            "dbo.UserRoleAccesses",
            c => new
                {
                    UserRoleAccessId = c.Long(nullable: false, identity: true),
                    UserRoleId = c.String(),
                    IsActive = c.Boolean(nullable: false),
                    Name = c.String(),
                    UserRoleAccessType = c.Int(nullable: false),
                    ApplicationRole_Id = c.String(maxLength: 128),
                })
            .PrimaryKey(t => t.UserRoleAccessId)
            .ForeignKey("dbo.AspNetRoles", t => t.ApplicationRole_Id)
            .Index(t => t.ApplicationRole_Id);

Полезные ссылки:

Введение в идентификацию ASP.NET - я создал простое решение MVC, использующее эту информацию.

Идентификация ASP.NET, выдержка из книги Pro ASP.NET MVC 5

person Diana    schedule 12.10.2016
comment
Проголосуйте за усилия! Спасибо! Хотя я не уверен, что вы полностью поняли, что я хотел сделать. Не могли бы вы отобразить изображение того, как выглядит ваша таблица UserRoleAccess? мне кажется, что у вас есть внешний ключ из таблицы AspNetUsers и таблицы AspnetRole. Я хочу иметь только ключевой атрибут из таблицы aspNetRoles. в методе CreateUserRoleAccess нам присваивается идентификатор роли, которая уже существует. И я постараюсь, чтобы вы попытались найти идентификатор, как вы предложили. - person ThunD3eR; 13.10.2016
comment
Добро пожаловать! Я добавил в ответ таблицу UserRoleAccess. Имеется внешний ключ из таблицы AspNetRoles, а не из таблицы AspNetUsers. - person Diana; 14.10.2016
comment
Итак, что вы хотите, так это использовать свойство UserRoleId в качестве поля внешнего ключа и ApplicationRole в качестве свойства навигации? Вы можете сделать это с помощью атрибута ForeignKey, например: [ForeignKey("UserRoleId")] public virtual ApplicationRole ApplicationRole { get; set; }. Вы можете поместить атрибут либо в свойство ApplicationRole, либо в свойство UserRoleId, просто параметром должно быть другое имя свойства. - person Diana; 14.10.2016
comment
Если вы это сделаете, поле ApplicationRole_Id больше не будет автоматически генерироваться (поскольку поле UserRoleId будет использоваться во внешнем ключе). Для ясности я рекомендую изменить имя UserRoleId на ApplicationRoleId. - person Diana; 14.10.2016
comment
Если вы предпочитаете плавное сопоставление аннотаций, вы можете сделать это вместо этого с помощью этого кода в методе OnModelCreating: modelBuilder.Entity<UserRoleAccess>().HasRequired(ura => ura.ApplicationRole) .WithMany().HasForeignKey(ura => ura.UserRoleId); - person Diana; 14.10.2016