Расширение ролей идентичности ASP.NET: IdentityRole не является частью модели для текущего контекста

Я пытаюсь использовать новую идентификатор ASP.NET в моем приложении MVC5, в частности, я пытаюсь интегрировать идентификатор ASP.NET в существующую базу данных. Я уже прочитал вопросы/ответы по SO, относящиеся к DB First и ASP.NET Identity, и выполнив все рекомендации, которые я по-прежнему не могу добавить роли в свою базу данных, хотя у меня нет проблем с добавлением пользователей. Здесь мой код:

var context = new PayrollDBEntities();
var roleManager = new RoleManager<AspNetRole>(new RoleStore<AspNetRole>(context));

bool roleExists = roleManager.RoleExists(roleDto.Name);
if (roleExists){
    return false;
}

var role = new AspNetRole(roleDto.Name){
    Name = roleDto.Name,
};

IdentityResult result = roleManager.Create(role);//Getting exception here

В последней строке кода я получаю исключение типа 'System.InvalidOperationException': The entity type IdentityRole is not part of the model for the current context.

Вот мой контекст:

public partial class PayrollDBEntities : IdentityDbContext
{
        public PayrollDBEntities()
            : base("name=PayrollDBEntities")
        {
        }

        public virtual DbSet<AspNetRole> AspNetRoles { get; set; }
        public virtual DbSet<AspNetUserClaim> AspNetUserClaims { get; set; }
        public virtual DbSet<AspNetUserLogin> AspNetUserLogins { get; set; }
        public virtual DbSet<AspNetUser> AspNetUsers { get; set; }
......
}

Мои классы AspNetUser и AspNetRole выводятся из IdentityUser и IdentityRole соответственно, но я все еще получаю это исключение. Вот моя диаграмма базы данных:

enter image description here

Любая помощь будет принята с благодарностью.

Ответ 1

После нескольких дней попыток заставить это работать без сбоев, я пришел к выводу, что если вы сначала используете Database и хотите интегрировать ASP.NET Identity в свое приложение, безусловно, самое простое и чистое решение является создание собственного поставщика членства путем переопределения ASP.NET Identity. Это на самом деле довольно легко, до сих пор я реализовал UserStore и RoleStore по своему вкусу. Я добавил столбцы/отношения, специфичные для моего домена, в мою базу данных, и всякий раз, когда я создаю пользователя или роль, я забочусь о фиксации своей базы данных, добавляя необходимые отношения. Моя реализация UserStore довольно похожа на это. Моя реализация RoleStore выглядит примерно так:

public class ApplicationRoleStore : IRoleStore<ApplicationRoleDTO>
{
    private PayrollDBEntities _context;
    public ApplicationRoleStore() { }

    public ApplicationRoleStore(PayrollDBEntities database)
    {
        _context = database;
    }

    public Task CreateAsync(ApplicationRoleDTO role)
    {
        if (role == null)
        {
            throw new ArgumentNullException("RoleIsRequired");
        }
        var roleEntity = ConvertApplicationRoleDTOToAspNetRole(role);
        _context.AspNetRoles.Add(roleEntity);
        return _context.SaveChangesAsync();

    }

    public Task DeleteAsync(ApplicationRoleDTO role)
    {
        var roleEntity = _context.AspNetRoles.FirstOrDefault(x => x.Id == role.Id);
        if (roleEntity == null) throw new InvalidOperationException("No such role exists!");
        _context.AspNetRoles.Remove(roleEntity);
        return _context.SaveChangesAsync();
    }

    public Task<ApplicationRoleDTO> FindByIdAsync(string roleId)
    {
        var role = _context.AspNetRoles.FirstOrDefault(x => x.Id == roleId);

        var result = role == null
            ? null
            : ConvertAspNetRoleToApplicationRoleDTO(role);

        return Task.FromResult(result);
    }

    public Task<ApplicationRoleDTO> FindByNameAsync(string roleName)
    {

        var role = _context.AspNetRoles.FirstOrDefault(x => x.Name == roleName);

        var result = role == null
            ? null
            : ConvertAspNetRoleToApplicationRoleDTO(role);

        return Task.FromResult(result);
    }

    public Task UpdateAsync(ApplicationRoleDTO role)
    {

        return _context.SaveChangesAsync();
    }

    public void Dispose()
    {
        _context.Dispose();
    }
    private ApplicationRoleDTO ConvertAspNetRoleToApplicationRoleDTO(AspNetRole aspRole)
    {
        return new ApplicationRoleDTO{
            Id = aspRole.Id,
            EnterpriseId = aspRole.EnterpriseId,
            Name = aspRole.Name
        };
    }

    private AspNetRole ConvertApplicationRoleDTOToAspNetRole(ApplicationRoleDTO appRole)
    {
        return new AspNetRole{
            Id = appRole.Id,
            EnterpriseId = appRole.EnterpriseId,
            Name = appRole.Name,
        };
    }
}

И мое приложение RoleDTO:

public class ApplicationRoleDTO : IRole
{
    public ApplicationRoleDTO()
    {
        Id = Guid.NewGuid().ToString();
    }

    public ApplicationRoleDTO(string roleName)
        : this()
    {
        Name = roleName;
    }
    public string Id { get; set; }
    public string Name { get; set; }
    public Guid EnterpriseId { get; set; }
}

Я также нашел эти 2 статьи довольно полезными:

Обзор пользовательских провайдеров хранения для удостоверения ASP.NET

Реализация пользовательского провайдера хранилища удостоверений MySQL ASP.NET

Ответ 2

При создании пользовательского хранилища вам необходимо указать, что вместо IdentityRole используется AspNetRole. Вы можете добиться этого, используя класс UserStore с 6 параметрами типа:

new UserStore<AspNetUser, AspNetRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>(new PayrollDBEntities());

Это также указывает на изменения в создании Менеджера пользователей. Ниже приведен упрощенный пример создания необходимых экземпляров:

public class AspNetUser : IdentityUser { /*customization*/ }

public class AspNetRole : IdentityRole { /*customization*/ }

public class PayrollDBEntities : IdentityDbContext //or : IdentityDbContext <AspNetUser, AspNetRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> 
{
}

public class Factory 
{
    public IdentityDbContext DbContext 
    { 
        get 
        {
            return new PayrollDBEntities();
        } 
    }

    public UserStore<AspNetUser, AspNetRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> UserStore
    {
        get 
        {                
            return new UserStore<AspNetUser, AspNetRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>(DbContext);
        }
    }

    public UserManager<AspNetUser, string> UserManager
    { 
        get 
        {
            return new UserManager<AspNetUser, string>(UserStore);
        } 
    }

    public RoleStore<AspNetRole> RoleStore 
    {
        get 
        {
            return new RoleStore<AspNetRole>(DbContext);
        }
    }

    public RoleManager<AspNetRole> RoleManager 
    {
        get 
        {
            return new RoleManager<AspNetRole>(RoleStore);
        }
    }
}

Ответ 3

Я объясню здесь с помощью примеров кода:).

Фокус в том, что они уже находятся в IdentityDbContext (AspNetRoles, AspNetUserClaims, AspNetUsers,....)

В IdentityModel вы увидите, что ApplicationUser пуст в верхней части. Если вы хотите настроить этих пользователей или роли, просто добавьте свойства здесь, а затем обновите свою базу данных с помощью консоли.

Пример моего контекста

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }

    public DbSet<Request> Requests { get; set; }
    public DbSet<Reservation> Reservations { get; set; }
    public DbSet<PriceType> PriceTypes { get; set; }
    public DbSet<Product> Products { get; set; }
    public DbSet<Price> Prices { get; set; }
    public DbSet<GuestbookPost> Posts { get; set; }
    public DbSet<Count> Counts { get; set; }
    public DbSet<Invoice> Invoices { get; set; }
    public DbSet<InvoiceLine> InvoiceLines { get; set; }

    ...

}

Таким образом, пользователь приложения не определен здесь, но я добавил к нему дополнительные свойства, например:

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string GroupName { get; set; }
    public string Email { get; set; }
    [StringLength(15)]
    public string Phone { get; set; }
    public string Remark { get; set; }
    public DateTime? BirthDate { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidUntil { get; set; }

    public string Street { get; set; }
    public string ZipCode { get; set; }
    public string City { get; set; }

    public virtual ICollection<Request> Requests { get; set; } 
}

Ответ 4

Я знаю, что это старый вопрос, но на случай, если кто-то еще с трудом добавляет роли/пользователей, когда они модифицируют идентификатор asp для использования числовых первичных ключей (int/long) вместо строки по умолчанию для ролей Identity, поэтому, если вы изменили IdentityUserRole в IdentityModels.cs на что-то вроде этого:

public class Role : IdentityRole<long, UserRole>
{
    public Role() { }
    public Role(string name) { Name = name; }
}

Вы должны использовать класс Role вместо стандартного IdentityRole при построении RoleManager, поэтому ваш код должен выглядеть примерно так:

public static void RegisterUserRoles()
{
     ApplicationDbContext context = new ApplicationDbContext();

     var RoleManager = new RoleManager<Role, long>(new RoleStore(context));

     if (!RoleManager.RoleExists("Administrador"))
     {
         var adminRole = new Role {
              Name = "Administrador",
         };
         RoleManager.Create(adminRole);
     }
}

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

Ответ 5

Я решил по-другому. Сначала я разделился на два разных проекта и контекстов. Мой проект, который обрабатывает идентичность, имеет такой контекст:

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDisposable
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

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

Это мой ApplicationUser:

public class ApplicationUser : IdentityUser
    {
        //Put here the extra properties that Identity does not handle
        [Required]
        [MaxLength(150)]
        public string Nome { get; set; }

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }
    }

И мой ApplicationUserManager выглядит так:

public class ApplicationUserManager : UserManager<ApplicationUser>
    {
        public ApplicationUserManager(IUserStore<ApplicationUser> store)
            : base(store)
        {
            //Setting validator to user name
            UserValidator = new UserValidator<ApplicationUser>(this)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };

            //Validation Logic and Password complexity 
            PasswordValidator = new PasswordValidator
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = false,
                RequireDigit = false,
                RequireLowercase = false,
                RequireUppercase = false,
            };

            //Lockout
            UserLockoutEnabledByDefault = true;
            DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
            MaxFailedAccessAttemptsBeforeLockout = 5;

            // Providers de Two Factor Autentication
            RegisterTwoFactorProvider("Código via SMS", new PhoneNumberTokenProvider<ApplicationUser>
            {
                MessageFormat = "Seu código de segurança é: {0}"
            });

            RegisterTwoFactorProvider("Código via E-mail", new EmailTokenProvider<ApplicationUser>
            {
                Subject = "Código de Segurança",
                BodyFormat = "Seu código de segurança é: {0}"
            });

            //Email service
            EmailService = new EmailService();

            // Definindo a classe de serviço de SMS
            SmsService = new SmsService();

            var provider = new DpapiDataProtectionProvider("Braian");
            var dataProtector = provider.Create("ASP.NET Identity");

            UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtector);

        }
    }

Я надеюсь, что это кому-то поможет. Это решение было из этой статьи: Эдуардо Пирес - но это на португальском

Ответ 6

Я исправил эту проблему, изменив свойство webconfig DefaultConnection connectionString, чтобы он указывал на новую базу данных SQLServer