Identitypersonalização de modelo no ASP.NET Core model customization in ASP.NET Core

Por Arthur VickerBy Arthur Vickers

ASP.NET Core Identity fornece uma estrutura para gerenciar e armazenar 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. Identityé adicionado ao seu projeto quando contas de usuário individuais é selecionada como o mecanismo de autenticação. is added to your project when Individual User Accounts is selected as the authentication mechanism. Por padrão, Identity o usa um modelo de dados principal do Entity Framework (EF).By default, Identity makes use of an Entity Framework (EF) Core data model. Este artigo descreve como personalizar o Identity modelo.This article describes how to customize the Identity model.

Identitye EF Core migrações and EF Core Migrations

Antes de examinar o modelo, é útil entender como o Identity funciona com EF Core migrações 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 um modelo de dados no código.Define or update a data model in code.
  2. Adicione uma migração para converter esse modelo em 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 se 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 a ser sincronizado com o modelo.Apply the Migration to update the database to be in sync with the model.
  5. Repita as etapas de 1 a 4 para refinar ainda mais o modelo 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 seguintes abordagens para adicionar e aplicar migrações:Use one of the following approaches to add and apply Migrations:

  • A janela do console do Gerenciador de pacotes (PMC) se estiver 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.
  • O CLI do .NET Core se estiver usando a linha de comando.The .NET Core CLI if using the command line. Para obter mais informações, consulte EF Core ferramentas de linha de comando do .net.For more information, see EF Core .NET command line tools.
  • Clicando no botão aplicar migrações na página de erro quando o aplicativo é executado.Clicking the Apply Migrations button on the error page when the app is run.

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. Os aplicativos de produção normalmente geram scripts SQL das migrações e implantam alterações no banco de dados como parte de uma implantação de banco de dados e aplicativo controlado.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 Identity for criado, as etapas 1 e 2 acima já foram 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 inicial já existe 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:

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

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

O Identity modeloThe Identity model

Tipos de entidadeEntity types

O Identity modelo consiste nos 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 a 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 usuários e funções.A join entity that associates users and roles.

Relações de tipo de entidadeEntity type relationships

Os tipos de entidade estão relacionados entre si das seguintes maneiras:The entity types are related to each other in the following ways:

  • Cada um User pode ter muitos UserClaims .Each User can have many UserClaims.
  • Cada um User pode ter muitos UserLogins .Each User can have many UserLogins.
  • Cada um User pode ter muitos UserTokens .Each User can have many UserTokens.
  • Cada um Role pode ter muitos associados RoleClaims .Each Role can have many associated RoleClaims.
  • Cada um User pode ter muitos associados Roles e cada um 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 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 pela UserRole entidade.The join table is represented by the UserRole entity.

Configuração de modelo padrãoDefault model configuration

Identitydefine muitas classes de contexto que herdam de DbContext para configurar e usar o modelo. defines many context classes that inherit from DbContext to configure and use the model. Essa configuração é feita usando o EF Core Code First API fluente no método OnModelCreating 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

Identitydefine os tipos de CLR ( Common Language Runtime ) padrão para cada um dos tipos de entidade listados acima. defines default Common Language Runtime (CLR) types for each of the entity types listed above. Esses tipos são todos prefixados com Identity :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 próprios tipos do aplicativo.Rather than using these types directly, the types can be used as base classes for the app's own types. As DbContext classes definidas por Identity são genéricas, de modo que diferentes tipos CLR podem ser usados para um ou mais 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 de chave primária (CP) seja alterado.These generic types also allow the User primary key (PK) data type to be changed.

Ao usar Identity com suporte para funções, uma 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 Identity sem funções (apenas declarações), caso em que uma 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 do modelo é derivar do tipo de contexto apropriado.The starting point for model customization is to derive from the appropriate context type. Consulte a seção tipos genéricos de modelo .See the Model generic types section. Esse tipo de contexto é normalmente chamado ApplicationDbContext e é criado pelos modelos de 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:

  • Fornecendo tipos de entidade e de chave 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 primeiro; 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. EF Core geralmente tem uma política do último-um-WINS para 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 for chamado primeiro com um nome de tabela e, em seguida, novamente mais tarde com um nome de tabela diferente, o nome da tabela na segunda chamada será 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 personalizadosCustom user data

Os dados de usuário personalizados têm suporte por herança do IdentityUser .Custom user data is supported by inheriting from IdentityUser. É personalizado nomear este tipo 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 na ApplicationDbContext classe.There's no need to override OnModelCreating in the ApplicationDbContext class. EF Core mapeia a CustomTag Propriedade por convenção.EF Core maps the CustomTag property by convention. No entanto, o banco de dados precisa ser atualizado para criar uma nova 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 Identity e EF Core migrações.To create the column, add a migration, and then update the database as described in Identity and EF Core Migrations.

Atualize as páginas/Shared/_LoginPartial. cshtml e substitua IdentityUser por 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

Atualizar áreas/ Identity /IdentityHostingStartup.cs ou Startup.ConfigureServices substituir IdentityUser por ApplicationUser .Update Areas/Identity/IdentityHostingStartup.cs or Startup.ConfigureServices and replace IdentityUser with ApplicationUser.

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

No ASP.NET Core 2,1 ou posterior, Identity é fornecido como uma Razor biblioteca de classes.In ASP.NET Core 2.1 or later, Identity is provided as a Razor Class Library. Para obter mais informações, consulte Scaffold Identity em projetos de ASP.NET Core.For more information, see Scaffold Identity em projetos de ASP.NET Core. Consequentemente, o código anterior requer uma chamada para AddDefaultUI .Consequently, the preceding code requires a call to AddDefaultUI. Se o Identity scaffolder foi usado para adicionar Identity arquivos ao 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 no tipo de dados da coluna CP depois que o banco de dado foi criado é problemática 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. A alteração da CP normalmente envolve a remoção e a recriação da tabela.Changing the PK typically involves dropping and re-creating the table. Portanto, os 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 CP:Follow these steps to change the PK type:

  1. Se o banco de dados foi criado antes da alteração de CP, 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. Atualize a ApplicationDbContext classe da qual derivar 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> devem ser especificadas 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> devem ser especificadas 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.ConfigureServicesdeve 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>();
    
    services.AddIdentity<IdentityUser<Guid>, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
    
    services.AddIdentity<IdentityUser<Guid>, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
            .AddDefaultTokenProviders();
    
  4. Se uma ApplicationUser classe personalizada estiver sendo usada, atualize a classe da qual herdar 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; }
    }
    

    Atualize ApplicationDbContext para fazer referência à ApplicationUser classe personalizada:Update ApplicationDbContext to reference the custom ApplicationUser class:

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

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

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

    O tipo de dados da chave primária é inferido pela análise do objeto DbContext .The primary key's data type is inferred by analyzing the DbContext object.

    No ASP.NET Core 2,1 ou posterior, Identity é fornecido como uma Razor biblioteca de classes.In ASP.NET Core 2.1 or later, Identity is provided as a Razor Class Library. Para obter mais informações, consulte Scaffold Identity em projetos de ASP.NET Core.For more information, see Scaffold Identity em projetos de ASP.NET Core. Consequentemente, o código anterior requer uma chamada para AddDefaultUI .Consequently, the preceding code requires a call to AddDefaultUI. Se o Identity scaffolder foi usado para adicionar Identity arquivos ao 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();
    

    O tipo de dados da chave primária é inferido pela análise do objeto DbContext .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 uma ApplicationRole classe personalizada estiver sendo usada, atualize a classe da qual herdar 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; }
    }
    

    Atualize ApplicationDbContext para fazer referência à ApplicationRole classe personalizada.Update ApplicationDbContext to reference the custom ApplicationRole class. Por exemplo, a seguinte classe faz referência a um personalizado ApplicationUser e a 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)
        {
        }
    }
    

    Registre a classe de contexto do banco de dados personalizado ao adicionar o Identity serviço 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);
    }
    

    O tipo de dados da chave primária é inferido pela análise do objeto DbContext .The primary key's data type is inferred by analyzing the DbContext object.

    No ASP.NET Core 2,1 ou posterior, Identity é fornecido como uma Razor biblioteca de classes.In ASP.NET Core 2.1 or later, Identity is provided as a Razor Class Library. Para obter mais informações, consulte Scaffold Identity em projetos de ASP.NET Core.For more information, see Scaffold Identity em projetos de ASP.NET Core. Consequentemente, o código anterior requer uma chamada para AddDefaultUI .Consequently, the preceding code requires a call to AddDefaultUI. Se o Identity scaffolder foi usado para adicionar Identity arquivos ao 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)
        {
        }
    }
    

    Registre a classe de contexto do banco de dados personalizado ao adicionar o Identity serviço 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>();
    }
    

    O tipo de dados da chave primária é inferido pela análise do objeto DbContext .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)
        {
        }
    }
    

    Registre a classe de contexto do banco de dados personalizado ao adicionar o Identity serviço 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 do modelo para relações pode ser mais difícil do que fazer 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 relações novas e adicionais.Care must be taken to replace the existing relationships rather than create new, additional relationships. Em particular, a relação alterada deve especificar a mesma propriedade de chave estrangeira (FK) que 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();
});

O FK para essa relação é especificado como a UserClaim.UserId propriedade.The FK for this relationship is specified as the UserClaim.UserId property. HasManye 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.

Adicione uma propriedade de navegação a ApplicationUser que permite que UserClaims os associados sejam referenciados 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 a CP de usuários.The TKey for IdentityUserClaim<TKey> is the type specified for the PK of users. Nesse caso, TKey o é string porque os padrões estão sendo usados.In this case, TKey is string because the defaults are being used. Não é o tipo de CP para o UserClaim tipo de entidade.It's not the PK type for the UserClaim entity type.

Agora que a propriedade de navegação existe, ela deve ser configurada em 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 está configurada exatamente como estava antes, somente 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 existem somente no modelo EF, não no banco de dados.The navigation properties only exist in the EF model, not the database. Como o FK da relação não foi alterado, esse tipo de alteração de modelo não exige que o banco de dados seja atualizado.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 adicionando uma migração depois de fazer a alteração.This can be checked by adding a migration after making the change. Os Up Down métodos e estão vazios.The Up and Down methods are empty.

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

Usando a seção acima como orientação, o exemplo a seguir configura as propriedades de navegação unidirecionais para todas as relações no 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 de usuário e 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 na 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();
        });

    }
}

Observações:Notes:

  • Este exemplo também inclui a UserRole entidade de junção, que é necessária para navegar na relação muitos para muitos de usuários para funções.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 para refletir que os 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 na ApplicationContext definição genérica.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 demonstraram a alteração do tipo de chave usada no Identity modelo.The preceding sections demonstrated changing the type of key used in the Identity model. IdentityNão há suporte ou recomendado para alterar o modelo de chave para usar chaves compostas.Changing the Identity key model to use composite keys isn't supported or recommended. Usar uma chave composta com Identity envolve a alteração de como o Identity código do Gerenciador 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 e facetas de tabela/colunaChange table/column names and facets

Para alterar os nomes de 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 Identity tabelas: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 Identity tipos padrão.These examples use the default Identity types. Se estiver usando um tipo de aplicativo como ApplicationUser , configure 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 determinadas facetas (por exemplo, o string comprimento máximo permitido).Some types of database columns can be configured with certain facets (for example, the maximum string length allowed). O exemplo a seguir define comprimentos máximos de 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

Os esquemas podem se comportar de forma diferente nos provedores de banco de dadosSchemas can behave differently across database providers. Por SQL Server, o padrão é criar todas as tabelas no esquema dbo .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 Identity modelo é adicionado.In this section, support for lazy-loading proxies in the Identity model is added. O carregamento lento é útil, pois permite que as propriedades de navegação sejam usadas sem primeiro garantir que estejam carregadas.Lazy-loading is useful since it allows navigation properties to be used without first ensuring they're loaded.

Tipos de entidade podem ser adequados para carregamento lento de várias 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, o que exige:For simplicity, use lazy-loading proxies, which requires:

O exemplo a seguir demonstra como chamar 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 aos tipos de entidade.Refer to the preceding examples for guidance on adding navigation properties to the entity types.

Recursos adicionaisAdditional resources