Identity Настройка модели в ASP.NET Core

Артур Викерс

ASP.NET Core Identity предоставляет платформу для управления учетными записями пользователей и их хранения в приложениях ASP.NET Core. Identity добавляется в проект при выборе отдельных учетных записей пользователей в качестве механизма проверки подлинности. По умолчанию Identity используется модель данных Entity Framework (EF) Core. В этой статье описывается настройка Identity модели.

Identity и EF Core миграции

Прежде чем изучать модель, полезно понять, как Identity работает с EF Core миграциями для создания и обновления базы данных. На верхнем уровне процесс выполняется следующим образом:

  1. Определение или обновление модели данных в коде.
  2. Добавьте миграцию для перевода этой модели в изменения, которые можно применить к базе данных.
  3. Убедитесь, что миграция правильно представляет ваши намерения.
  4. Примените миграцию, чтобы обновить базу данных для синхронизации с моделью.
  5. Повторите шаги 1–4, чтобы дополнительно уточнить модель и сохранить базу данных в синхронизации.

Используйте один из следующих подходов для добавления и применения миграций.

  • Окно консоли диспетчер пакетов (PMC) при использовании Visual Studio. Дополнительные сведения см. в статье EF Core о средствах PMC.
  • Интерфейс командной строки .NET Core при использовании командной строки. Дополнительные сведения см. в EF Core средствах командной строки .NET.
  • Нажатие кнопки "Применить миграцию" на странице ошибки при запуске приложения.

ASP.NET Core имеет обработчик страницы ошибок во время разработки. Обработчик может применять миграции при запуске приложения. Рабочие приложения обычно создают скрипты SQL из миграций и развертывают изменения базы данных в рамках управляемого развертывания приложения и базы данных.

После создания нового приложения Identity шаги 1 и 2 выше уже завершены. То есть исходная модель данных уже существует, а начальная миграция была добавлена в проект. Начальная миграция по-прежнему должна применяться к базе данных. Начальная миграция может применяться с помощью одного из следующих подходов:

  • Запустите Update-Database в PMC.
  • Выполните dotnet ef database update в командной оболочке.
  • Нажмите кнопку "Применить миграцию" на странице ошибки при запуске приложения.

Повторите предыдущие шаги по мере внесения изменений в модель.

Модель Identity

Типы сущностей

Модель Identity состоит из следующих типов сущностей.

Тип объекта Description
User Представляет пользователя.
Role Представляет роль.
UserClaim Представляет утверждение о том, что пользователь обладает.
UserToken Представляет маркер проверки подлинности для пользователя.
UserLogin Связывает пользователя с именем входа.
RoleClaim Представляет утверждение, которое предоставляется всем пользователям в роли.
UserRole Сущность присоединения, которая связывает пользователей и ролей.

Связи типов сущностей

Типы сущностей связаны друг с другом следующими способами:

  • Каждый User может иметь много UserClaims.
  • Каждый User может иметь много UserLogins.
  • Каждый User может иметь много UserTokens.
  • Каждый из них Role может иметь множество связанных RoleClaims.
  • Каждый User из них может иметь много связанных Roles, и каждый из них Role может быть связан с многими Users. Это связь "многие ко многим", требующая таблицы соединения в базе данных. Таблица соединения представлена сущностью UserRole .

Конфигурация модели по умолчанию

Identity определяет множество классов контекста, наследующих от DbContext настройки и использования модели. Эта конфигурация выполняется с помощью EF Core API Code First Fluent в OnModelCreating методе класса контекста. Конфигурация по умолчанию:

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");
});

Универсальные типы модели

Identityопределяет типы среды CLR по умолчанию для каждого из типов сущностей, перечисленных выше. Эти типы имеют префикс:Identity

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

Вместо прямого использования этих типов типы можно использовать в качестве базовых классов для собственных типов приложения. Классы DbContext , определенные универсальными Identity , поэтому различные типы СРЕДЫ CLR можно использовать для одного или нескольких типов сущностей в модели. Эти универсальные типы также позволяют User изменять тип данных первичного ключа (PK).

При использовании Identity с поддержкой IdentityDbContext ролей следует использовать класс. Например:

// 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>

Кроме того, можно использовать Identity без ролей (только утверждений IdentityUserContext<TUser> ), в этом случае следует использовать класс:

// 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>
{
}

Настройка модели

Отправной точкой настройки модели является производный от соответствующего типа контекста. См. раздел "Универсальные типы модели". Этот тип контекста обычно вызывается ApplicationDbContext и создается шаблонами ASP.NET Core.

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

  • Предоставление типов сущностей и ключей для параметров универсального типа.
  • Переопределение OnModelCreating для изменения сопоставления этих типов.

При переопределении сначала следует вызвать конфигурацию переопределенияOnModelCreatingbase.OnModelCreating. EF Core Обычно для конфигурации имеется политика последней победы. Например, если ToTable метод для типа сущности вызывается сначала с одним именем таблицы, а затем еще раз с другим именем таблицы, используется имя таблицы во втором вызове.

ПРИМЕЧАНИЕ. Если DbContext не является производным от IdentityDbContext, AddEntityFrameworkStores может не выводить правильные типы POCO для TUserClaim, TUserLoginи TUserToken. Если AddEntityFrameworkStores не выводить правильные типы POCO, решение заключается в том, чтобы напрямую добавить правильные типы с помощью services.AddScoped<IUser/RoleStore<TUser> и UserStore<...>>.

Пользовательские данные пользователя

Пользовательские пользовательские данные поддерживаются путем наследования от IdentityUser. Это обычно называется следующим типом ApplicationUser:

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

ApplicationUser Используйте тип в качестве универсального аргумента для контекста:

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

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

В классе не нужно переопределяться OnModelCreatingApplicationDbContext . EF Core сопоставляет свойство по соглашению CustomTag . Однако для создания нового CustomTag столбца необходимо обновить базу данных. Чтобы создать столбец, добавьте миграцию и обновите базу данных, как описано в Identity разделе "EF CoreМиграция".

Обновление Pages/Shared/_LoginPartial.cshtml и замена IdentityUser на ApplicationUser:

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

Обновление Areas/Identity/IdentityHostingStartup.cs или Startup.ConfigureServices замена IdentityUserApplicationUserна .

services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();                                    

Вызов AddDefaultIdentity эквивалентен следующему коду:

services.AddAuthentication(o =>
{
    o.DefaultScheme = IdentityConstants.ApplicationScheme;
    o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies(o => { });

services.AddIdentityCore<TUser>(o =>
{
    o.Stores.MaxLengthForKeys = 128;
    o.SignIn.RequireConfirmedAccount = true;
})
.AddDefaultUI()
.AddDefaultTokenProviders();

Identity предоставляется как Razor библиотека классов. Дополнительные сведения см. в разделе Шаблоны Identity в проектах ASP.NET Core. Следовательно, для предыдущего кода требуется вызов AddDefaultUI. Identity Если шаблон использовался для добавления Identity файлов в проект, удалите вызовAddDefaultUI. Дополнительные сведения см. в разделе:

Изменение типа первичного ключа

Изменение типа данных столбца PK после создания базы данных проблематично во многих системах баз данных. Изменение PK обычно включает удаление и повторное создание таблицы. Поэтому при создании базы данных необходимо указать типы ключей при первоначальной миграции.

Выполните следующие действия, чтобы изменить тип PK:

  1. Если база данных была создана перед изменением PK, выполните команду Drop-Database (PMC) или dotnet ef database drop (.NET Core CLI), чтобы удалить ее.

  2. После подтверждения удаления базы данных удалите начальную миграцию с помощью Remove-Migration PMC или dotnet ef migrations remove (.NET Core CLI).

  3. Обновите класс, производный ApplicationDbContext от IdentityDbContext<TUser,TRole,TKey>. Укажите новый тип ключа для TKey. Например, чтобы использовать Guid тип ключа:

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

    В предыдущем коде универсальные классы IdentityUser<TKey> и IdentityRole<TKey> должны быть указаны для использования нового типа ключа.

    Startup.ConfigureServices необходимо обновить для использования универсального пользователя:

    services.AddDefaultIdentity<IdentityUser<Guid>>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();
    
  4. Если используется пользовательский ApplicationUser класс, обновите класс, чтобы наследовать от IdentityUser. Например:

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

    Обновите ApplicationDbContext ссылку на пользовательский ApplicationUser класс:

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

    Зарегистрируйте пользовательский класс контекста базы данных при добавлении Identity службы в Startup.ConfigureServices:

    services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();
    

    Тип данных первичного ключа определяется путем анализа DbContext объекта.

    Identity предоставляется как Razor библиотека классов. Дополнительные сведения см. в разделе Шаблоны Identity в проектах ASP.NET Core. Следовательно, для предыдущего кода требуется вызов AddDefaultUI. Identity Если шаблон использовался для добавления Identity файлов в проект, удалите вызовAddDefaultUI.

  5. Если используется пользовательский ApplicationRole класс, обновите класс, чтобы наследовать от IdentityRole<TKey>. Например:

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

    Обновите ApplicationDbContext ссылку на пользовательский ApplicationRole класс. Например, следующий класс ссылается на пользовательскую ApplicationUser и пользовательскую 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)
        {
        }
    }
    

    Зарегистрируйте пользовательский класс контекста базы данных при добавлении Identity службы в 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);
    }
    

    Тип данных первичного ключа определяется путем анализа DbContext объекта.

    Identity предоставляется как Razor библиотека классов. Дополнительные сведения см. в разделе Шаблоны Identity в проектах ASP.NET Core. Следовательно, для предыдущего кода требуется вызов AddDefaultUI. Identity Если шаблон использовался для добавления Identity файлов в проект, удалите вызовAddDefaultUI.

Добавление свойств навигации

Изменение конфигурации модели для связей может быть сложнее, чем внесение других изменений. Необходимо учесть, чтобы заменить существующие связи, а не создать новые, дополнительные связи. В частности, измененная связь должна указывать то же свойство внешнего ключа (FK), что и существующая связь. Например, связь между Users и UserClaims имеет значение по умолчанию, указанное следующим образом:

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

FK для этой связи указывается в качестве UserClaim.UserId свойства. HasMany и WithOne вызываются без аргументов для создания связи без свойств навигации.

Добавьте свойство навигации, ApplicationUser которое позволяет ссылаться на связанный UserClaims пользователь:

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

IdentityUserClaim<TKey> ТипTKey, указанный для PK пользователей. В этом случае TKey используется string значение по умолчанию. Это не тип PK для типа сущностиUserClaim.

Теперь, когда существует свойство навигации, его необходимо настроить в 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();
        });
    }
}

Обратите внимание, что связь настроена точно так же, как и раньше, только с свойством навигации, указанным в вызове HasMany.

Свойства навигации существуют только в модели EF, а не в базе данных. Так как FK для связи не изменился, такое изменение модели не требует обновления базы данных. Это можно проверка, добавив миграцию после внесения изменений. Down Методы Up пусты.

Добавление всех свойств навигации пользователей

Используя приведенный выше раздел в качестве руководства, в следующем примере настраивается однонаправленная навигация для всех связей пользователя:

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();
        });
    }
}

Добавление свойств навигации пользователей и ролей

Используя приведенный выше раздел в качестве руководства, в следующем примере настраивается свойства навигации для всех связей с пользователем и ролью:

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();
        });

    }
}

Примечания

  • В этом примере также содержится UserRole сущность соединения, которая необходима для перехода к связи "многие ко многим" от пользователей к ролям.
  • Не забудьте изменить типы свойств навигации, чтобы отразить, что Application{...} типы теперь используются вместо Identity{...} типов.
  • Не забудьте использовать Application{...} в универсальном ApplicationContext определении.

Добавление всех свойств навигации

Используя приведенный выше раздел в качестве руководства, следующий пример настраивает свойства навигации для всех связей для всех типов сущностей:

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();
        });
    }
}

Использование составных ключей

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

Изменение имен таблиц и столбцов и аспектов

Чтобы изменить имена таблиц и столбцов, вызовите base.OnModelCreating. Затем добавьте конфигурацию для переопределения любого из значений по умолчанию. Например, чтобы изменить имя всех Identity таблиц:

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");
    });
}

В этих примерах используются типы по умолчанию Identity . При использовании типа приложения, ApplicationUserнапример, настройте этот тип вместо типа по умолчанию.

В следующем примере изменяются некоторые имена столбцов:

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");
    });
}

Некоторые типы столбцов базы данных можно настроить с определенными аспектами (например, максимально string допустимой длиной). Следующий пример задает максимальную длину столбца для нескольких string свойств в модели:

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);
    });
}

Сопоставление с другой схемой

Схемы могут вести себя по-разному между поставщиками баз данных. Для SQL Server по умолчанию создается все таблицы в схеме dbo . Таблицы можно создать в другой схеме. Например:

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

    modelBuilder.HasDefaultSchema("notdbo");
}

Отложенная загрузка

В этом разделе добавлена поддержка отложенных прокси-серверов в Identity модели. Отложенная загрузка полезна, так как позволяет использовать свойства навигации без предварительной загрузки.

Типы сущностей можно использовать для отложенной загрузки несколькими способами, как описано в EF Core документации. Для простоты используйте отложенные прокси-серверы, которые требуют:

В следующем примере демонстрируется вызов UseLazyLoadingProxies в Startup.ConfigureServices:

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

Дополнительные сведения о добавлении свойств навигации в типы сущностей см. в приведенных выше примерах.

Дополнительные ресурсы