| | | 1 | | using Microsoft.AspNetCore.Identity; |
| | | 2 | | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; |
| | | 3 | | using Microsoft.EntityFrameworkCore; |
| | | 4 | | using OzonDomains.Models.AccessControl; |
| | | 5 | | |
| | | 6 | | namespace OzonRepositories.Context.Identity |
| | | 7 | | { |
| | | 8 | | public class OzonIdentityOrderContext : IdentityDbContext<CustomIdentityUser> |
| | | 9 | | { |
| | 4 | 10 | | public OzonIdentityOrderContext(DbContextOptions<OzonIdentityOrderContext> options) : base(options) { } |
| | | 11 | | |
| | 2 | 12 | | public DbSet<AccessProfile> AccessProfiles { get; set; } |
| | | 13 | | |
| | | 14 | | protected override void OnModelCreating(ModelBuilder builder) |
| | | 15 | | { |
| | 1 | 16 | | base.OnModelCreating(builder); |
| | | 17 | | |
| | | 18 | | // AddEntityFrameworkStores в DI выставляет IdentityOptions.Stores.MaxLengthForKeys = 128, |
| | | 19 | | // и боевые таблицы созданы именно с этой длиной. Design-time factory поднимает |
| | | 20 | | // контекст без DI, где значение по умолчанию — 0 (то есть varchar(255)). |
| | | 21 | | // Фиксируем длины явно, чтобы модель не зависела от способа сборки контекста. |
| | 1 | 22 | | builder.Entity<IdentityUserLogin<string>>(e => |
| | 1 | 23 | | { |
| | 1 | 24 | | e.Property(x => x.LoginProvider).HasMaxLength(128); |
| | 1 | 25 | | e.Property(x => x.ProviderKey).HasMaxLength(128); |
| | 2 | 26 | | }); |
| | 1 | 27 | | builder.Entity<IdentityUserToken<string>>(e => |
| | 1 | 28 | | { |
| | 1 | 29 | | e.Property(x => x.LoginProvider).HasMaxLength(128); |
| | 1 | 30 | | e.Property(x => x.Name).HasMaxLength(128); |
| | 2 | 31 | | }); |
| | | 32 | | |
| | | 33 | | // AccessProfile тянет за собой DataScopeSet/DataScopeSetItem и join-таблицу. |
| | | 34 | | // Без явных имён EF выводил их в единственном числе (DataScopeSet, |
| | | 35 | | // DataScopeSetItem, AccessProfileDataScopeSet) — таких таблиц в БД нет, |
| | | 36 | | // и модель этого контекста расходилась с ApplicationDbContext. |
| | | 37 | | // |
| | | 38 | | // Владелец этих четырёх таблиц — ApplicationDbContext, он их и создаёт. |
| | | 39 | | // ExcludeFromMigrations не даёт второму набору миграций создать их повторно: |
| | | 40 | | // оба контекста работают с одной и той же базой. |
| | 1 | 41 | | builder.Entity<AccessProfile>() |
| | 2 | 42 | | .ToTable("AccessProfiles", t => t.ExcludeFromMigrations()); |
| | 1 | 43 | | builder.Entity<DataScopeSet>() |
| | 2 | 44 | | .ToTable("DataScopeSets", t => t.ExcludeFromMigrations()); |
| | 1 | 45 | | builder.Entity<DataScopeSetItem>() |
| | 2 | 46 | | .ToTable("DataScopeSetItems", t => t.ExcludeFromMigrations()); |
| | 1 | 47 | | builder.Entity<AccessProfile>() |
| | 1 | 48 | | .HasMany(ap => ap.DataScopeSets) |
| | 1 | 49 | | .WithMany(ds => ds.AccessProfiles) |
| | 3 | 50 | | .UsingEntity(j => j.ToTable("AccessProfileDataScopeSets", t => t.ExcludeFromMigrations())); |
| | | 51 | | |
| | | 52 | | // В production-базе AspNetUsers встречаются legacy-строки с пустым Discriminator. |
| | | 53 | | // IsComplete(false) добавляет фильтрацию по известным типам и не даёт EF падать |
| | | 54 | | // при чтении строк с неизвестным/пустым значением дискриминатора. |
| | 1 | 55 | | builder.Entity<CustomIdentityUser>() |
| | 1 | 56 | | .HasDiscriminator<string>("Discriminator") |
| | 1 | 57 | | .IsComplete(false) |
| | 1 | 58 | | .HasValue<CustomIdentityUser>(nameof(CustomIdentityUser)); |
| | | 59 | | |
| | 1 | 60 | | builder.Entity<CustomIdentityUser>() |
| | 1 | 61 | | .HasOne(u => u.AccessProfile) |
| | 1 | 62 | | .WithMany() |
| | 1 | 63 | | .HasForeignKey(u => u.AccessProfileId) |
| | 1 | 64 | | .OnDelete(DeleteBehavior.SetNull); |
| | | 65 | | |
| | 1 | 66 | | if (ApplicationDbContext.IsMySqlProvider(Database.ProviderName)) |
| | | 67 | | { |
| | 1 | 68 | | ProdSchemaAlignment.Apply(builder); |
| | | 69 | | } |
| | 1 | 70 | | } |
| | | 71 | | } |
| | | 72 | | } |