Personalização de modelo de identidade no ASP.NET CoreIdentity model customization in ASP.NET Core

Por Arthur VickersBy Arthur Vickers

Identidade do ASP.NET Core fornece uma estrutura para gerenciar e armazenar as contas de usuário em aplicativos ASP.NET Core.ASP.NET Core Identity provides a framework for managing and storing user accounts in ASP.NET Core apps. Identidade é adicionada ao seu projeto quando contas de usuário individuais é selecionado como o mecanismo de autenticação.Identity is added to your project when Individual User Accounts is selected as the authentication mechanism. Por padrão, a identidade faz usar de um Entity Framework (EF) modelo de dados central.By default, Identity makes use of an Entity Framework (EF) Core data model. Este artigo descreve como personalizar o modelo de identidade.This article describes how to customize the Identity model.

Identidade e migrações do EF CoreIdentity and EF Core Migrations

Antes de examinar o modelo, ele é útil para entender como a identidade funciona com migrações do EF Core para criar e atualizar um banco de dados.Before examining the model, it's useful to understand how Identity works with EF Core Migrations to create and update a database. No nível superior, o processo é:At the top level, the process is:

  1. Definir ou atualizar uma modelo de dados no código.Define or update a data model in code.
  2. Adicione uma migração para traduzir esse modelo para as alterações que podem ser aplicadas ao banco de dados.Add a Migration to translate this model into changes that can be applied to the database.
  3. Verifique que a migração representa corretamente suas intenções.Check that the Migration correctly represents your intentions.
  4. Aplique a migração para atualizar o banco de dados para ser sincronizado com o modelo.Apply the Migration to update the database to be in sync with the model.
  5. Repita as etapas 1 a 4 para refinar o modelo ainda mais e manter o banco de dados em sincronia.Repeat steps 1 through 4 to further refine the model and keep the database in sync.

Use uma das abordagens a seguir para adicionar e aplicar as migrações:Use one of the following approaches to add and apply Migrations:

  • O Package Manager Console janela (PMC) se usando o Visual Studio.The Package Manager Console (PMC) window if using Visual Studio. Para obter mais informações, consulte ferramentas do EF Core PMC.For more information, see EF Core PMC tools.
  • A CLI do .NET Core se usando a linha de comando.The .NET Core CLI if using the command line. Para obter mais informações, consulte ferramentas de linha de comando do EF Core .NET.For more information, see EF Core .NET command line tools.
  • Clicar a aplicar migrações botão na página de erro quando o aplicativo é executado.Clicking the Apply Migrations button on the error page when the app is run.

O ASP.NET Core tem um manipulador de página de erro de tempo de desenvolvimento.ASP.NET Core has a development-time error page handler. O manipulador pode aplicar migrações quando o aplicativo é executado.The handler can apply migrations when the app is run. Aplicativos de produção normalmente geram scripts SQL das migrações e implantar as alterações do banco de dados como parte de um aplicativo controlado e a implantação de banco de dados.Production apps typically generate SQL scripts from the migrations and deploy database changes as part of a controlled app and database deployment.

Quando um novo aplicativo usando a identidade é criado, as etapas 1 e 2 acima já tem sido concluídas.When a new app using Identity is created, steps 1 and 2 above have already been completed. Ou seja, o modelo de dados iniciais já existir e a migração inicial foi adicionada ao projeto.That is, the initial data model already exists, and the initial migration has been added to the project. A migração inicial ainda precisa ser aplicada ao banco de dados.The initial migration still needs to be applied to the database. A migração inicial pode ser aplicada por meio de uma das seguintes abordagens:The initial migration can be applied via one of the following approaches:

  • Execute Update-Database no PMC.Run Update-Database in PMC.
  • Executar dotnet ef database update em um shell de comando.Run dotnet ef database update in a command shell.
  • Clique o aplicar migrações botão na página de erro quando o aplicativo é executado.Click the Apply Migrations button on the error page when the app is run.

Repita as etapas anteriores, como as alterações são feitas no modelo.Repeat the preceding steps as changes are made to the model.

O modelo de identidadeThe Identity model

Tipos de entidadeEntity types

O modelo de identidade consiste dos seguintes tipos de entidade.The Identity model consists of the following entity types.

Tipo de entidadeEntity type DescriçãoDescription
User Representa o usuário.Represents the user.
Role Representa uma função.Represents a role.
UserClaim Representa uma declaração que um usuário possui.Represents a claim that a user possesses.
UserToken Representa um token de autenticação para um usuário.Represents an authentication token for a user.
UserLogin Associa um usuário com um logon.Associates a user with a login.
RoleClaim Representa uma declaração que é concedida a todos os usuários dentro de uma função.Represents a claim that's granted to all users within a role.
UserRole Uma entidade de junção que associa os usuários e funções.A join entity that associates users and roles.

Relacionamentos de tipo de entidadeEntity type relationships

O tipos de entidade estão relacionados uns aos outros das seguintes maneiras:The entity types are related to each other in the following ways:

  • Cada User pode ter muitas UserClaims.Each User can have many UserClaims.
  • Cada User pode ter muitas UserLogins.Each User can have many UserLogins.
  • Cada User pode ter muitas UserTokens.Each User can have many UserTokens.
  • Cada Role pode ter muitas associado RoleClaims.Each Role can have many associated RoleClaims.
  • Cada User pode ter muitas associados Rolese cada Role pode ser associado a muitos Users.Each User can have many associated Roles, and each Role can be associated with many Users. Essa é uma relação de muitos-para-muitos que requer uma tabela de junção no banco de dados.This is a many-to-many relationship that requires a join table in the database. A tabela de junção é representada pelo UserRole entidade.The join table is represented by the UserRole entity.

Configuração de modelo padrãoDefault model configuration

Identidade define muitos classes de contexto que herdam DbContext para configurar e usar o modelo.Identity defines many context classes that inherit from DbContext to configure and use the model. Essa configuração é feita usando o EF Core Fluent API do Code First na OnModelCreating método da classe de contexto.This configuration is done using the EF Core Code First Fluent API in the OnModelCreating method of the context class. A configuração padrão é:The default configuration is:

builder.Entity<TUser>(b =>
{
    // Primary key
    b.HasKey(u => u.Id);

    // Indexes for "normalized" username and email, to allow efficient lookups
    b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique();
    b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex");

    // Maps to the AspNetUsers table
    b.ToTable("AspNetUsers");

    // A concurrency token for use with the optimistic concurrency checking
    b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();

    // Limit the size of columns to use efficient database types
    b.Property(u => u.UserName).HasMaxLength(256);
    b.Property(u => u.NormalizedUserName).HasMaxLength(256);
    b.Property(u => u.Email).HasMaxLength(256);
    b.Property(u => u.NormalizedEmail).HasMaxLength(256);

    // The relationships between User and other entity types
    // Note that these relationships are configured with no navigation properties

    // Each User can have many UserClaims
    b.HasMany<TUserClaim>().WithOne().HasForeignKey(uc => uc.UserId).IsRequired();

    // Each User can have many UserLogins
    b.HasMany<TUserLogin>().WithOne().HasForeignKey(ul => ul.UserId).IsRequired();

    // Each User can have many UserTokens
    b.HasMany<TUserToken>().WithOne().HasForeignKey(ut => ut.UserId).IsRequired();

    // Each User can have many entries in the UserRole join table
    b.HasMany<TUserRole>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
});

builder.Entity<TUserClaim>(b =>
{
    // Primary key
    b.HasKey(uc => uc.Id);

    // Maps to the AspNetUserClaims table
    b.ToTable("AspNetUserClaims");
});

builder.Entity<TUserLogin>(b =>
{
    // Composite primary key consisting of the LoginProvider and the key to use
    // with that provider
    b.HasKey(l => new { l.LoginProvider, l.ProviderKey });

    // Limit the size of the composite key columns due to common DB restrictions
    b.Property(l => l.LoginProvider).HasMaxLength(128);
    b.Property(l => l.ProviderKey).HasMaxLength(128);

    // Maps to the AspNetUserLogins table
    b.ToTable("AspNetUserLogins");
});

builder.Entity<TUserToken>(b =>
{
    // Composite primary key consisting of the UserId, LoginProvider and Name
    b.HasKey(t => new { t.UserId, t.LoginProvider, t.Name });

    // Limit the size of the composite key columns due to common DB restrictions
    b.Property(t => t.LoginProvider).HasMaxLength(maxKeyLength);
    b.Property(t => t.Name).HasMaxLength(maxKeyLength);

    // Maps to the AspNetUserTokens table
    b.ToTable("AspNetUserTokens");
});

builder.Entity<TRole>(b =>
{
    // Primary key
    b.HasKey(r => r.Id);

    // Index for "normalized" role name to allow efficient lookups
    b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex").IsUnique();

    // Maps to the AspNetRoles table
    b.ToTable("AspNetRoles");

    // A concurrency token for use with the optimistic concurrency checking
    b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();

    // Limit the size of columns to use efficient database types
    b.Property(u => u.Name).HasMaxLength(256);
    b.Property(u => u.NormalizedName).HasMaxLength(256);

    // The relationships between Role and other entity types
    // Note that these relationships are configured with no navigation properties

    // Each Role can have many entries in the UserRole join table
    b.HasMany<TUserRole>().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();

    // Each Role can have many associated RoleClaims
    b.HasMany<TRoleClaim>().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
});

builder.Entity<TRoleClaim>(b =>
{
    // Primary key
    b.HasKey(rc => rc.Id);

    // Maps to the AspNetRoleClaims table
    b.ToTable("AspNetRoleClaims");
});

builder.Entity<TUserRole>(b =>
{
    // Primary key
    b.HasKey(r => new { r.UserId, r.RoleId });

    // Maps to the AspNetUserRoles table
    b.ToTable("AspNetUserRoles");
});

Tipos genéricos de modeloModel generic types

Identidade define o padrão Common Language Runtime tipos (CLR) para cada um dos tipos de entidade listados acima.Identity defines default Common Language Runtime (CLR) types for each of the entity types listed above. Esses tipos são prefixados com identidade:These types are all prefixed with Identity:

  • IdentityUser
  • IdentityRole
  • IdentityUserClaim
  • IdentityUserToken
  • IdentityUserLogin
  • IdentityRoleClaim
  • IdentityUserRole

Em vez de usar esses tipos diretamente, os tipos podem ser usados como classes base para os tipos do aplicativo.Rather than using these types directly, the types can be used as base classes for the app's own types. O DbContext classes definidas pela identidade são genéricas, de modo que diferentes tipos CLR podem ser usados para uma ou mais dos tipos de entidade no modelo.The DbContext classes defined by Identity are generic, such that different CLR types can be used for one or more of the entity types in the model. Esses tipos genéricos também permitem que o User tipo de dados (PK) chave primária a ser alterado.These generic types also allow the User primary key (PK) data type to be changed.

Ao usar a identidade com suporte para funções, um IdentityDbContext classe deve ser usada.When using Identity with support for roles, an IdentityDbContext class should be used. Por exemplo:For example:

// Uses all the built-in Identity types
// Uses `string` as the key type
public class IdentityDbContext
    : IdentityDbContext<IdentityUser, IdentityRole, string>
{
}

// Uses the built-in Identity types except with a custom User type
// Uses `string` as the key type
public class IdentityDbContext<TUser>
    : IdentityDbContext<TUser, IdentityRole, string>
        where TUser : IdentityUser
{
}

// Uses the built-in Identity types except with custom User and Role types
// The key type is defined by TKey
public class IdentityDbContext<TUser, TRole, TKey> : IdentityDbContext<
    TUser, TRole, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>,
    IdentityUserLogin<TKey>, IdentityRoleClaim<TKey>, IdentityUserToken<TKey>>
        where TUser : IdentityUser<TKey>
        where TRole : IdentityRole<TKey>
        where TKey : IEquatable<TKey>
{
}

// No built-in Identity types are used; all are specified by generic arguments
// The key type is defined by TKey
public abstract class IdentityDbContext<
    TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>
    : IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>
         where TUser : IdentityUser<TKey>
         where TRole : IdentityRole<TKey>
         where TKey : IEquatable<TKey>
         where TUserClaim : IdentityUserClaim<TKey>
         where TUserRole : IdentityUserRole<TKey>
         where TUserLogin : IdentityUserLogin<TKey>
         where TRoleClaim : IdentityRoleClaim<TKey>
         where TUserToken : IdentityUserToken<TKey>

Também é possível usar a identidade sem funções (apenas declarações), caso em que um IdentityUserContext<TUser> classe deve ser usada:It's also possible to use Identity without roles (only claims), in which case an IdentityUserContext<TUser> class should be used:

// Uses the built-in non-role Identity types except with a custom User type
// Uses `string` as the key type
public class IdentityUserContext<TUser>
    : IdentityUserContext<TUser, string>
        where TUser : IdentityUser
{
}

// Uses the built-in non-role Identity types except with a custom User type
// The key type is defined by TKey
public class IdentityUserContext<TUser, TKey> : IdentityUserContext<
    TUser, TKey, IdentityUserClaim<TKey>, IdentityUserLogin<TKey>,
    IdentityUserToken<TKey>>
        where TUser : IdentityUser<TKey>
        where TKey : IEquatable<TKey>
{
}

// No built-in Identity types are used; all are specified by generic arguments, with no roles
// The key type is defined by TKey
public abstract class IdentityUserContext<
    TUser, TKey, TUserClaim, TUserLogin, TUserToken> : DbContext
        where TUser : IdentityUser<TKey>
        where TKey : IEquatable<TKey>
        where TUserClaim : IdentityUserClaim<TKey>
        where TUserLogin : IdentityUserLogin<TKey>
        where TUserToken : IdentityUserToken<TKey>
{
}

Personalizar o modeloCustomize the model

O ponto de partida para a personalização de modelo é derivado do tipo de contexto apropriado.The starting point for model customization is to derive from the appropriate context type. Consulte a modelar tipos genéricos seção.See the Model generic types section. Esse tipo de contexto geralmente é chamado ApplicationDbContext e é criada pelos modelos do ASP.NET Core.This context type is customarily called ApplicationDbContext and is created by the ASP.NET Core templates.

O contexto é usado para configurar o modelo de duas maneiras:The context is used to configure the model in two ways:

  • Fornecimento de entidade e tipos de chaves para os parâmetros de tipo genérico.Supplying entity and key types for the generic type parameters.
  • Substituindo OnModelCreating para modificar o mapeamento desses tipos.Overriding OnModelCreating to modify the mapping of these types.

Ao substituir OnModelCreating, base.OnModelCreating deve ser chamado pela primeira vez; a configuração de substituição deve ser chamada em seguida.When overriding OnModelCreating, base.OnModelCreating should be called first; the overriding configuration should be called next. O EF Core geralmente tem uma política last-one-wins para a configuração.EF Core generally has a last-one-wins policy for configuration. Por exemplo, se o ToTable método para um tipo de entidade é chamado pela primeira vez com o nome de uma tabela e, em seguida, novamente mais tarde com um nome de tabela diferente, o nome da tabela na segunda chamada é usado.For example, if the ToTable method for an entity type is called first with one table name and then again later with a different table name, the table name in the second call is used.

Dados de usuário personalizadaCustom user data

Dados de usuário personalizados há suporte para herdar de IdentityUser.Custom user data is supported by inheriting from IdentityUser. É comum para esse tipo de nome ApplicationUser:It's customary to name this type ApplicationUser:

public class ApplicationUser : IdentityUser
{
    public string CustomTag { get; set; }
}

Use o ApplicationUser tipo como um argumento genérico para o contexto:Use the ApplicationUser type as a generic argument for the context:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}

Não é necessário substituir OnModelCreating no ApplicationDbContext classe.There's no need to override OnModelCreating in the ApplicationDbContext class. O EF Core mapeia o CustomTag propriedade por convenção.EF Core maps the CustomTag property by convention. No entanto, o banco de dados precisa ser atualizado para criar um novo CustomTag coluna.However, the database needs to be updated to create a new CustomTag column. Para criar a coluna, adicione uma migração e, em seguida, atualize o banco de dados, conforme descrito em identidade e migrações do EF Core.To create the column, add a migration, and then update the database as described in Identity and EF Core Migrations.

Atualização Pages/Shared/_LoginPartial.cshtml e substitua IdentityUser com ApplicationUser:Update Pages/Shared/_LoginPartial.cshtml and replace IdentityUser with ApplicationUser:

@using Microsoft.AspNetCore.Identity
@using WebApp1.Areas.Identity.Data
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

Atualização Areas/Identity/IdentityHostingStartup.cs ou Startup.ConfigureServices e substituir IdentityUser com ApplicationUser.Update Areas/Identity/IdentityHostingStartup.cs or Startup.ConfigureServices and replace IdentityUser with ApplicationUser.

services.AddDefaultIdentity<ApplicationUser>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultUI();

No ASP.NET Core 2.1 ou posterior, a identidade é fornecida como uma biblioteca de classes Razor.In ASP.NET Core 2.1 or later, Identity is provided as a Razor Class Library. Para obter mais informações, consulte Identidade Scaffold em projetos ASP.NET Core.For more information, see Identidade Scaffold em projetos ASP.NET Core. Consequentemente, o código anterior requer uma chamada para AddDefaultUI.Consequently, the preceding code requires a call to AddDefaultUI. Se o scaffolder de identidade foi usado para adicionar arquivos de identidade para o projeto, remova a chamada para AddDefaultUI.If the Identity scaffolder was used to add Identity files to the project, remove the call to AddDefaultUI. Para obter mais informações, consulte:For more information, see:

Alterar o tipo de chave primáriaChange the primary key type

Uma alteração para o tipo de dados da coluna PK depois que o banco de dados foi criado é um problema em muitos sistemas de banco de dados.A change to the PK column's data type after the database has been created is problematic on many database systems. Alterando a PK normalmente envolve descartar e recriar a tabela.Changing the PK typically involves dropping and re-creating the table. Portanto, tipos de chave devem ser especificados na migração inicial quando o banco de dados é criado.Therefore, key types should be specified in the initial migration when the database is created.

Siga estas etapas para alterar o tipo de PK:Follow these steps to change the PK type:

  1. Se o banco de dados foi criado antes da alteração de PK, execute Drop-Database (PMC) ou dotnet ef database drop (CLI do .NET Core) para excluí-lo.If the database was created before the PK change, run Drop-Database (PMC) or dotnet ef database drop (.NET Core CLI) to delete it.

  2. Depois de confirmar a exclusão do banco de dados, remova a migração inicial com Remove-Migration (PMC) ou dotnet ef migrations remove (CLI do .NET Core).After confirming deletion of the database, remove the initial migration with Remove-Migration (PMC) or dotnet ef migrations remove (.NET Core CLI).

  3. Atualizar o ApplicationDbContext classe para derivar de IdentityDbContext<TUser,TRole,TKey>.Update the ApplicationDbContext class to derive from IdentityDbContext<TUser,TRole,TKey>. Especifique o novo tipo de chave para TKey.Specify the new key type for TKey. Por exemplo, para usar um Guid tipo de chave:For example, to use a Guid key type:

    public class ApplicationDbContext
        : IdentityDbContext<IdentityUser<Guid>, IdentityRole<Guid>, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    No código anterior, as classes genéricas IdentityUser<TKey> e IdentityRole<TKey> deve ser especificado para usar o novo tipo de chave.In the preceding code, the generic classes IdentityUser<TKey> and IdentityRole<TKey> must be specified to use the new key type.

    No código anterior, as classes genéricas IdentityUser<TKey> e IdentityRole<TKey> deve ser especificado para usar o novo tipo de chave.In the preceding code, the generic classes IdentityUser<TKey> and IdentityRole<TKey> must be specified to use the new key type.

    Startup.ConfigureServices deve ser atualizado para usar o usuário genérico:Startup.ConfigureServices must be updated to use the generic user:

    services.AddDefaultIdentity<IdentityUser<Guid>>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
    
    services.AddIdentity<IdentityUser<Guid>, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
    
    services.AddIdentity<IdentityUser<Guid>, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
            .AddDefaultTokenProviders();
    
  4. Se um personalizado ApplicationUser classe está sendo usado, atualize a classe para herdar de IdentityUser.If a custom ApplicationUser class is being used, update the class to inherit from IdentityUser. Por exemplo:For example:

    using System;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    
    public class ApplicationUser : IdentityUser<Guid>
    {
        public string CustomTag { get; set; }        
    }
    
    using System;
    using Microsoft.AspNetCore.Identity;
    
    public class ApplicationUser : IdentityUser<Guid>
    {
        public string CustomTag { get; set; }
    }
    

    Atualização ApplicationDbContext para fazer referência a personalizada ApplicationUser classe:Update ApplicationDbContext to reference the custom ApplicationUser class:

    public class ApplicationDbContext
        : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    Registrar a classe de contexto do banco de dados personalizados ao adicionar o serviço de identidade no Startup.ConfigureServices:Register the custom database context class when adding the Identity service in Startup.ConfigureServices:

    services.AddDefaultIdentity<ApplicationUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultUI()
            .AddDefaultTokenProviders();
    

    Tipo de dados da chave primária é inferido, analisando as DbContext objeto.The primary key's data type is inferred by analyzing the DbContext object.

    No ASP.NET Core 2.1 ou posterior, a identidade é fornecida como uma biblioteca de classes Razor.In ASP.NET Core 2.1 or later, Identity is provided as a Razor Class Library. Para obter mais informações, consulte Identidade Scaffold em projetos ASP.NET Core.For more information, see Identidade Scaffold em projetos ASP.NET Core. Consequentemente, o código anterior requer uma chamada para AddDefaultUI.Consequently, the preceding code requires a call to AddDefaultUI. Se o scaffolder de identidade foi usado para adicionar arquivos de identidade para o projeto, remova a chamada para AddDefaultUI.If the Identity scaffolder was used to add Identity files to the project, remove the call to AddDefaultUI.

    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
    

    Tipo de dados da chave primária é inferido, analisando as DbContext objeto.The primary key's data type is inferred by analyzing the DbContext object.

    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
            .AddDefaultTokenProviders();
    

    O AddEntityFrameworkStores método aceita um TKey tipo que indica o tipo de dados da chave primária.The AddEntityFrameworkStores method accepts a TKey type indicating the primary key's data type.

  5. Se um personalizado ApplicationRole classe está sendo usado, atualize a classe para herdar de IdentityRole<TKey>.If a custom ApplicationRole class is being used, update the class to inherit from IdentityRole<TKey>. Por exemplo:For example:

    using System;
    using Microsoft.AspNetCore.Identity;
    
    public class ApplicationRole : IdentityRole<Guid>
    {
        public string Description { get; set; }
    }
    

    Atualização ApplicationDbContext para fazer referência a personalizada ApplicationRole classe.Update ApplicationDbContext to reference the custom ApplicationRole class. Por exemplo, a classe a seguir faz referência a um personalizado ApplicationUser e um personalizado ApplicationRole:For example, the following class references a custom ApplicationUser and a custom ApplicationRole:

    using System;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore;
    
    public class ApplicationDbContext :
        IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    Registrar a classe de contexto do banco de dados personalizados ao adicionar o serviço de identidade no Startup.ConfigureServices:Register the custom database context class when adding the Identity service in Startup.ConfigureServices:

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });
    
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
    
        services.AddIdentity<ApplicationUser, ApplicationRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultUI()
                .AddDefaultTokenProviders();
    
        services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }
    

    Tipo de dados da chave primária é inferido, analisando as DbContext objeto.The primary key's data type is inferred by analyzing the DbContext object.

    No ASP.NET Core 2.1 ou posterior, a identidade é fornecida como uma biblioteca de classes Razor.In ASP.NET Core 2.1 or later, Identity is provided as a Razor Class Library. Para obter mais informações, consulte Identidade Scaffold em projetos ASP.NET Core.For more information, see Identidade Scaffold em projetos ASP.NET Core. Consequentemente, o código anterior requer uma chamada para AddDefaultUI.Consequently, the preceding code requires a call to AddDefaultUI. Se o scaffolder de identidade foi usado para adicionar arquivos de identidade para o projeto, remova a chamada para AddDefaultUI.If the Identity scaffolder was used to add Identity files to the project, remove the call to AddDefaultUI.

    using System;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore;
    
    public class ApplicationDbContext : 
        IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    Registrar a classe de contexto do banco de dados personalizados ao adicionar o serviço de identidade no Startup.ConfigureServices:Register the custom database context class when adding the Identity service in Startup.ConfigureServices:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
    
        services.AddIdentity<ApplicationUser, ApplicationRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();
    
        services.AddMvc()
            .AddRazorPagesOptions(options =>
            {
                options.Conventions.AuthorizeFolder("/Account/Manage");
                options.Conventions.AuthorizePage("/Account/Logout");
            });
    
        services.AddSingleton<IEmailSender, EmailSender>();
    }
    

    Tipo de dados da chave primária é inferido, analisando as DbContext objeto.The primary key's data type is inferred by analyzing the DbContext object.

    using System;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore;
    
    public class ApplicationDbContext : 
        IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    Registrar a classe de contexto do banco de dados personalizados ao adicionar o serviço de identidade no Startup.ConfigureServices:Register the custom database context class when adding the Identity service in Startup.ConfigureServices:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options => 
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
    
        services.AddIdentity<ApplicationUser, ApplicationRole>()
                .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
                .AddDefaultTokenProviders();
    
        services.AddMvc();
    
        services.AddTransient<IEmailSender, AuthMessageSender>();
        services.AddTransient<ISmsSender, AuthMessageSender>();
    }
    

    O AddEntityFrameworkStores método aceita um TKey tipo que indica o tipo de dados da chave primária.The AddEntityFrameworkStores method accepts a TKey type indicating the primary key's data type.

Adicionar propriedades de navegaçãoAdd navigation properties

Alterar a configuração de modelo para relações pode ser mais difícil do que fazendo outras alterações.Changing the model configuration for relationships can be more difficult than making other changes. Deve-se ter cuidado para substituir as relações existentes em vez de criar novas relações adicionais.Care must be taken to replace the existing relationships rather than create new, additional relationships. Em particular, a relação alterada deve especificar o mesmo (FK) propriedade de chave estrangeira como a relação existente.In particular, the changed relationship must specify the same foreign key (FK) property as the existing relationship. Por exemplo, a relação entre Users e UserClaims é, por padrão, especificada da seguinte maneira:For example, the relationship between Users and UserClaims is, by default, specified as follows:

builder.Entity<TUser>(b =>
{
    // Each User can have many UserClaims
    b.HasMany<TUserClaim>()
     .WithOne()
     .HasForeignKey(uc => uc.UserId)
     .IsRequired();
});

A FK para essa relação é especificada como o UserClaim.UserId propriedade.The FK for this relationship is specified as the UserClaim.UserId property. HasMany e WithOne são chamados sem argumentos para criar a relação sem propriedades de navegação.HasMany and WithOne are called without arguments to create the relationship without navigation properties.

Adicionar uma propriedade de navegação ApplicationUser que permite que associado UserClaims seja referenciado do usuário:Add a navigation property to ApplicationUser that allows associated UserClaims to be referenced from the user:

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
}

O TKey para IdentityUserClaim<TKey> é o tipo especificado para o PK de usuários.The TKey for IdentityUserClaim<TKey> is the type specified for the PK of users. Nesse caso, TKey é string porque os padrões estão sendo usados.In this case, TKey is string because the defaults are being used. Ele tem não o tipo de PK para o UserClaim tipo de entidade.It's not the PK type for the UserClaim entity type.

Agora que existe a propriedade de navegação, ele deve ser configurado no OnModelCreating:Now that the navigation property exists, it must be configured in OnModelCreating:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<ApplicationUser>(b =>
        {
            // Each User can have many UserClaims
            b.HasMany(e => e.Claims)
                .WithOne()
                .HasForeignKey(uc => uc.UserId)
                .IsRequired();
        });
    }
}

Observe que a relação é configurada exatamente como ele era antes, apenas com uma propriedade de navegação especificada na chamada para HasMany.Notice that relationship is configured exactly as it was before, only with a navigation property specified in the call to HasMany.

As propriedades de navegação só existem no modelo do EF, não o banco de dados.The navigation properties only exist in the EF model, not the database. Como a FK para a relação não foi alterado, esse tipo de alteração de modelo não exige o banco de dados a ser atualizada.Because the FK for the relationship hasn't changed, this kind of model change doesn't require the database to be updated. Isso pode ser verificado com a adição de uma migração após fazer a alteração.This can be checked by adding a migration after making the change. O Up e Down métodos estão vazios.The Up and Down methods are empty.

Adicionar todas as propriedades de navegação do usuárioAdd all User navigation properties

O exemplo a seguir usando a seção acima como orientação, configura as propriedades de navegação unidirecional para todas as relações em usuário:Using the section above as guidance, the following example configures unidirectional navigation properties for all relationships on User:

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
    public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
    public virtual ICollection<IdentityUserToken<string>> Tokens { get; set; }
    public virtual ICollection<IdentityUserRole<string>> UserRoles { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<ApplicationUser>(b =>
        {
            // Each User can have many UserClaims
            b.HasMany(e => e.Claims)
                .WithOne()
                .HasForeignKey(uc => uc.UserId)
                .IsRequired();

            // Each User can have many UserLogins
            b.HasMany(e => e.Logins)
                .WithOne()
                .HasForeignKey(ul => ul.UserId)
                .IsRequired();

            // Each User can have many UserTokens
            b.HasMany(e => e.Tokens)
                .WithOne()
                .HasForeignKey(ut => ut.UserId)
                .IsRequired();

            // Each User can have many entries in the UserRole join table
            b.HasMany(e => e.UserRoles)
                .WithOne()
                .HasForeignKey(ur => ur.UserId)
                .IsRequired();
        });
    }
}

Adicionar propriedades de navegação do usuário e a funçãoAdd User and Role navigation properties

Usando a seção acima como orientação, o exemplo a seguir configura as propriedades de navegação para todas as relações no usuário e a função:Using the section above as guidance, the following example configures navigation properties for all relationships on User and Role:

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
    public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
    public virtual ICollection<IdentityUserToken<string>> Tokens { get; set; }
    public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}

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

public class ApplicationUserRole : IdentityUserRole<string>
{
    public virtual ApplicationUser User { get; set; }
    public virtual ApplicationRole Role { get; set; }
}
public class ApplicationDbContext
    : IdentityDbContext<
        ApplicationUser, ApplicationRole, string,
        IdentityUserClaim<string>, ApplicationUserRole, IdentityUserLogin<string>,
        IdentityRoleClaim<string>, IdentityUserToken<string>>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<ApplicationUser>(b =>
        {
            // Each User can have many UserClaims
            b.HasMany(e => e.Claims)
                .WithOne()
                .HasForeignKey(uc => uc.UserId)
                .IsRequired();

            // Each User can have many UserLogins
            b.HasMany(e => e.Logins)
                .WithOne()
                .HasForeignKey(ul => ul.UserId)
                .IsRequired();

            // Each User can have many UserTokens
            b.HasMany(e => e.Tokens)
                .WithOne()
                .HasForeignKey(ut => ut.UserId)
                .IsRequired();

            // Each User can have many entries in the UserRole join table
            b.HasMany(e => e.UserRoles)
                .WithOne(e => e.User)
                .HasForeignKey(ur => ur.UserId)
                .IsRequired();
        });

        modelBuilder.Entity<ApplicationRole>(b =>
        {
            // Each Role can have many entries in the UserRole join table
            b.HasMany(e => e.UserRoles)
                .WithOne(e => e.Role)
                .HasForeignKey(ur => ur.RoleId)
                .IsRequired();
        });

    }
}

Notas:Notes:

  • Este exemplo também inclui o UserRole entidade, que é necessário para navegar pela relação muitos-para-muitos dos usuários às funções de ingresso.This example also includes the UserRole join entity, which is needed to navigate the many-to-many relationship from Users to Roles.
  • Lembre-se de alterar os tipos das propriedades de navegação de refletir isso ApplicationXxx tipos agora estão sendo usados em vez de IdentityXxx tipos.Remember to change the types of the navigation properties to reflect that ApplicationXxx types are now being used instead of IdentityXxx types.
  • Lembre-se de usar o ApplicationXxx no genérico ApplicationContext definição.Remember to use the ApplicationXxx in the generic ApplicationContext definition.

Adicionar todas as propriedades de navegaçãoAdd all navigation properties

Usando a seção acima como orientação, o exemplo a seguir configura as propriedades de navegação para todas as relações em todos os tipos de entidade:Using the section above as guidance, the following example configures navigation properties for all relationships on all entity types:

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<ApplicationUserClaim> Claims { get; set; }
    public virtual ICollection<ApplicationUserLogin> Logins { get; set; }
    public virtual ICollection<ApplicationUserToken> Tokens { get; set; }
    public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}

public class ApplicationRole : IdentityRole
{
    public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
    public virtual ICollection<ApplicationRoleClaim> RoleClaims { get; set; }
}

public class ApplicationUserRole : IdentityUserRole<string>
{
    public virtual ApplicationUser User { get; set; }
    public virtual ApplicationRole Role { get; set; }
}

public class ApplicationUserClaim : IdentityUserClaim<string>
{
    public virtual ApplicationUser User { get; set; }
}

public class ApplicationUserLogin : IdentityUserLogin<string>
{
    public virtual ApplicationUser User { get; set; }
}

public class ApplicationRoleClaim : IdentityRoleClaim<string>
{
    public virtual ApplicationRole Role { get; set; }
}

public class ApplicationUserToken : IdentityUserToken<string>
{
    public virtual ApplicationUser User { get; set; }
}
public class ApplicationDbContext
    : IdentityDbContext<
        ApplicationUser, ApplicationRole, string,
        ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin,
        ApplicationRoleClaim, ApplicationUserToken>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<ApplicationUser>(b =>
        {
            // Each User can have many UserClaims
            b.HasMany(e => e.Claims)
                .WithOne(e => e.User)
                .HasForeignKey(uc => uc.UserId)
                .IsRequired();

            // Each User can have many UserLogins
            b.HasMany(e => e.Logins)
                .WithOne(e => e.User)
                .HasForeignKey(ul => ul.UserId)
                .IsRequired();

            // Each User can have many UserTokens
            b.HasMany(e => e.Tokens)
                .WithOne(e => e.User)
                .HasForeignKey(ut => ut.UserId)
                .IsRequired();

            // Each User can have many entries in the UserRole join table
            b.HasMany(e => e.UserRoles)
                .WithOne(e => e.User)
                .HasForeignKey(ur => ur.UserId)
                .IsRequired();
        });

        modelBuilder.Entity<ApplicationRole>(b =>
        {
            // Each Role can have many entries in the UserRole join table
            b.HasMany(e => e.UserRoles)
                .WithOne(e => e.Role)
                .HasForeignKey(ur => ur.RoleId)
                .IsRequired();

            // Each Role can have many associated RoleClaims
            b.HasMany(e => e.RoleClaims)
                .WithOne(e => e.Role)
                .HasForeignKey(rc => rc.RoleId)
                .IsRequired();
        });
    }
}

Usar chaves compostasUse composite keys

As seções anteriores demonstrou como alterar o tipo da chave usada no modelo de identidade.The preceding sections demonstrated changing the type of key used in the Identity model. Alterando o modelo de chave de identidade para usar chaves compostas não é suportado nem recomendado.Changing the Identity key model to use composite keys isn't supported or recommended. Usar uma chave composta com identidade envolve a alteração como o código do Gerenciador de identidade interage com o modelo.Using a composite key with Identity involves changing how the Identity manager code interacts with the model. Essa personalização está além do escopo deste documento.This customization is beyond the scope of this document.

Alterar nomes de tabela/coluna e as facetasChange table/column names and facets

Para alterar os nomes das tabelas e colunas, chame base.OnModelCreating.To change the names of tables and columns, call base.OnModelCreating. Em seguida, adicione a configuração para substituir qualquer um dos padrões.Then, add configuration to override any of the defaults. Por exemplo, para alterar o nome de todas as tabelas de identidade:For example, to change the name of all the Identity tables:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<IdentityUser>(b =>
    {
        b.ToTable("MyUsers");
    });

    modelBuilder.Entity<IdentityUserClaim<string>>(b =>
    {
        b.ToTable("MyUserClaims");
    });

    modelBuilder.Entity<IdentityUserLogin<string>>(b =>
    {
        b.ToTable("MyUserLogins");
    });

    modelBuilder.Entity<IdentityUserToken<string>>(b =>
    {
        b.ToTable("MyUserTokens");
    });

    modelBuilder.Entity<IdentityRole>(b =>
    {
        b.ToTable("MyRoles");
    });

    modelBuilder.Entity<IdentityRoleClaim<string>>(b =>
    {
        b.ToTable("MyRoleClaims");
    });

    modelBuilder.Entity<IdentityUserRole<string>>(b =>
    {
        b.ToTable("MyUserRoles");
    });
}

Esses exemplos usam os tipos de identidade padrão.These examples use the default Identity types. Se usar um tipo de aplicativo, como ApplicationUser, configurar esse tipo em vez do tipo padrão.If using an app type such as ApplicationUser, configure that type instead of the default type.

O exemplo a seguir altera alguns nomes de coluna:The following example changes some column names:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<IdentityUser>(b =>
    {
        b.Property(e => e.Email).HasColumnName("EMail");
    });

    modelBuilder.Entity<IdentityUserClaim<string>>(b =>
    {
        b.Property(e => e.ClaimType).HasColumnName("CType");
        b.Property(e => e.ClaimValue).HasColumnName("CValue");
    });
}

Alguns tipos de colunas de banco de dados podem ser configurados com determinados facetas (por exemplo, o máximo string comprimento permitido).Some types of database columns can be configured with certain facets (for example, the maximum string length allowed). O exemplo a seguir define os comprimentos máximos da coluna para várias string propriedades no modelo:The following example sets column maximum lengths for several string properties in the model:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<IdentityUser>(b =>
    {
        b.Property(u => u.UserName).HasMaxLength(128);
        b.Property(u => u.NormalizedUserName).HasMaxLength(128);
        b.Property(u => u.Email).HasMaxLength(128);
        b.Property(u => u.NormalizedEmail).HasMaxLength(128);
    });

    modelBuilder.Entity<IdentityUserToken<string>>(b =>
    {
        b.Property(t => t.LoginProvider).HasMaxLength(128);
        b.Property(t => t.Name).HasMaxLength(128);
    });
}

Mapear para um esquema diferenteMap to a different schema

Esquemas podem se comportar de maneira diferente em provedores de banco de dados.Schemas can behave differently across database providers. Para o SQL Server, o padrão é criar todas as tabelas na dbo esquema.For SQL Server, the default is to create all tables in the dbo schema. As tabelas podem ser criadas em um esquema diferente.The tables can be created in a different schema. Por exemplo:For example:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.HasDefaultSchema("notdbo");
}

Carregamento lentoLazy loading

Nesta seção, o suporte para proxies de carregamento lento no modelo de identidade é adicionado.In this section, support for lazy-loading proxies in the Identity model is added. Carregamento lento é útil, pois ele permite que as propriedades de navegação a ser usada sem primeiro garantir que eles são carregados.Lazy-loading is useful since it allows navigation properties to be used without first ensuring they're loaded.

Tipos de entidade podem ser feitos adequados para carregamento lento de diversas maneiras, conforme descrito na documentação do EF Core.Entity types can be made suitable for lazy-loading in several ways, as described in the EF Core documentation. Para simplificar, use proxies de carregamento lento, que requer:For simplicity, use lazy-loading proxies, which requires:

O exemplo a seguir demonstra a chamada UseLazyLoadingProxies em Startup.ConfigureServices:The following example demonstrates calling UseLazyLoadingProxies in Startup.ConfigureServices:

services
    .AddDbContext<ApplicationDbContext>(
        b => b.UseSqlServer(connectionString)
              .UseLazyLoadingProxies())
    .AddDefaultIdentity<ApplicationUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

Consulte os exemplos anteriores para obter orientação sobre como adicionar propriedades de navegação para os tipos de entidade.Refer to the preceding examples for guidance on adding navigation properties to the entity types.

Recursos adicionaisAdditional resources