web-dev-qa-db-ja.com

.NET Core 2.x Identity int外部キーはint主キーをターゲットにできません

.net core 2.xを実行しているWebアプリケーション(ついに.net core 1.xからアップグレードされたばかり)があり、.net core 1.xから2.xに移植されたほとんどすべてのものを手に入れました。ただし、Identityの実装でレンガの壁にぶつかりました。 .netコア1.xでは問題なく動作しましたが、現在は拒否されています。

実装はEntity Framework Coreで実行され、最初にデータベースを構築します(既存のデータベースに実装されます)。

私の問題は、.net core 2.xでログインしようとすると、次のようなエラーメッセージが表示されることです。

InvalidOperationException:外部キープロパティ{'RoleId':int}を持つ 'AspNetUserRole.AspNetRole'から 'AspNetRole.AspNetUserRoles'への関係は、互換性がないため、主キー{'Id':int}をターゲットにできません。この関係のプリンシパルキーまたは互換性のある外部キープロパティのセットを構成します。

これは、私にはまったく意味がありません。 int外部キーがint主キーと互換性がないのはなぜですか?

コンテキストとクラスの実際の実装は次のとおりです(これは愚かな単純な実装です)。

public partial class AspNetUser : IdentityUser<int>
{ }
public partial class AspNetRole : IdentityRole<int>
{ }
public partial class AspNetRoleClaim : IdentityRoleClaim<int>
{ }
public partial class AspNetUserClaim : IdentityUserClaim<int>
{ }
public partial class AspNetUserRole : IdentityUserRole<int>
{ }
public partial class AspNetUserToken : IdentityUserToken<int>
{ }
public partial class AspNetUserLogin : IdentityUserLogin<int>
{ }

public class IdentityDataContext : IdentityDbContext<AspNetUser, AspNetRole, int, AspNetUserClaim, AspNetUserRole, AspNetUserLogin, AspNetRoleClaim, AspNetUserToken>
{
    public IdentityDataContext(DbContextOptions<IdentityDataContext> options) : base(options)
    { }

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

        builder.Entity<AspNetUser>()
            .HasMany(e => e.AspNetUserClaims)
            .WithOne()
            .HasForeignKey(e => e.UserId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

        builder.Entity<AspNetUserClaim>()
           .HasOne(x => x.AspNetUser)
           .WithMany(x => x.AspNetUserClaims)
           .HasForeignKey(x => x.UserId);

        builder.Entity<AspNetUser>()
            .HasMany(e => e.AspNetUserLogins)
            .WithOne()
            .HasForeignKey(e => e.UserId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

        builder.Entity<AspNetUserLogin>()
           .HasOne(x => x.AspNetUser)
           .WithMany(x => x.AspNetUserLogins)
           .HasForeignKey(x => x.UserId);

        builder.Entity<AspNetUser>()
            .HasMany(e => e.AspNetUserRoles)
            .WithOne()
            .HasForeignKey(e => e.UserId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

        builder.Entity<AspNetUserRole>()
           .HasOne(x => x.AspNetUser)
           .WithMany(x => x.AspNetUserRoles)
           .HasForeignKey(x => x.UserId);

        builder.Entity<AspNetRole>()
            .HasMany(e => e.AspNetUserRoles)
            .WithOne()
            .HasForeignKey(e => e.RoleId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

        builder.Entity<AspNetUserRole>()
            .HasOne(x => x.AspNetRole)
            .WithMany(x => x.AspNetUserRoles)
            .HasForeignKey(x => x.RoleId);

        builder.Entity<AspNetUserRole>()
            .HasOne(x => x.AspNetUser)
            .WithMany(x => x.AspNetUserRoles)
            .HasForeignKey(x => x.UserId);

        builder.Entity<AspNetRole>()
            .HasMany(e => e.AspNetRoleClaims)
            .WithOne()
            .HasForeignKey(e => e.RoleId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

        builder.Entity<AspNetRoleClaim>()
           .HasOne(x => x.AspNetRole)
           .WithMany(x => x.AspNetRoleClaims)
           .HasForeignKey(x => x.RoleId);

        builder.Entity<AspNetUser>()
            .HasMany(e => e.AspNetUserTokens)
            .WithOne()
            .HasForeignKey(e => e.UserId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

        builder.Entity<AspNetUserToken>()
           .HasOne(x => x.AspNetUser)
           .WithMany(x => x.AspNetUserTokens)
           .HasForeignKey(x => x.UserId);
    }
}

そしてそれが文句を言う2つのクラスは次のように定義されます:

[Table("AspNetUserRoles")]
public partial class AspNetUserRole
{
    [Key]
    public int Id { get; set; }
    [ForeignKey("AspNetUser")]
    public override int UserId { get; set; }
    [ForeignKey("AspNetRole")]
    public override int RoleId { get; set; }
    public string ConcurrencyStamp { get; set; }
    public int CreatedById { get; set; }
    public System.DateTime CreatedDate { get; set; }
    public Nullable<int> ChangedById { get; set; }
    public Nullable<System.DateTime> ChangedDate { get; set; }
    public bool IsDisabled { get; set; }

    [JsonIgnore]
    public virtual AspNetRole AspNetRole { get; set; }
    [JsonIgnore]
    public virtual AspNetUser AspNetUser { get; set; }
}

[Table("AspNetRoles")]
public partial class AspNetRole
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public AspNetRole()
    {
        this.AspNetRoleClaims = new HashSet<AspNetRoleClaim>();
        this.AspNetUserRoles = new HashSet<AspNetUserRole>();
    }

    [Key]
    public override int Id { get; set; }
    public override string Name { get; set; }
    public override string NormalizedName { get; set; }
    public override string ConcurrencyStamp { get; set; }
    public int CreatedById { get; set; }
    public System.DateTime CreatedDate { get; set; }
    public Nullable<int> ChangedById { get; set; }
    public Nullable<System.DateTime> ChangedDate { get; set; }
    public bool IsDisabled { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<AspNetRoleClaim> AspNetRoleClaims { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<AspNetUserRole> AspNetUserRoles { get; set; }
}

これは実際に私を完全に困惑させています。

編集:エラーをスローする場所でDbSetを壊して評価しようとすると、ModelEvaluatorを指すスタックトレースが表示されます。正直なところ、私にはほとんど役に立ちません。

microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateNoShadowKeys(IModel model)\ r\n at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model)\ r\n at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate\r\n Microsoft.EntityFrameworkCore.Internal.SqlServerModelValidator.Validate(IModel model)\ r\n at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context、IConventionSetBuilder ConventionSetBuilder、IModelValidator validator)\ r\n System.Lazyに'1.ViaFactory(LazyThreadSafetyMode mode)\ r\n ---例外がスローされた前の場所からのスタックトレースの終わり---\r\n System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()で\ r\n
at System.Lazy'1.CreateValue()\ r\n at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()\ r\n at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()\ r\nでMicrosoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite、ServiceProviderEngineScope scope)\ r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructorScopeCallProtSiteScopeConstructor(ProviderEngineSstruct) Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite、ServiceProviderEngineScope scope)\ r\n at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider、Type serviceTypeProviderService.InviderServices.ProviderService.Indition.ProviderService.Indition.ProviderProviderProviderProviderProviderProviderProviderService.Indition.ProviderProviderExtensionsProdition GetRequiredService [T](IServiceProvider provider)\ r\n Microsoft.EntityFrameでworkCore.DbContext.get_DbContextDependencies()\ r\n at Microsoft.EntityFrameworkCore.DbContext.get_Model()\ r\n at Microsoft.EntityFrameworkCore.Internal.InternalDbSet'1.get_EntityType()\ r\n at Microsoft.EntityFrameworkCore.Internal。 InternalDbSet'1.get_EntityQueryable()\ r\n at Microsoft.EntityFrameworkCore.Internal.InternalDbSet'1.System.Collections.Generic.IEnumerable.GetEnumerator()\ r\n at System.Collections.Generic.LargeArrayBuilder'1.AddRange( IEnumerable'1項目)\ r\n System.Collections.Generic.EnumerableHelpers.ToArray [T](IEnumerable'1 source)\ r\n at System.Linq.Enumerable.ToArray [TSource](IEnumerable'1 source)\ r\n System.Linq.SystemCore_EnumerableDebugView'1.get_Items()で

編集2:推奨事項として、さまざまな外部キーを定義する場所にOnModelCreatingを追加してみました(上記のIdentityDataContextの定義を参照)-運が悪い.

編集3:OnModelCreatingは答えでした:もしそうなら、私は「逆転」の定義を見逃していたところです。たとえば、定義

builder.Entity<AspNetUser>()
    .HasMany(e => e.AspNetUserRoles)
    .WithOne()
    .HasForeignKey(e => e.UserId)
    .IsRequired()
    .OnDelete(DeleteBehavior.Restrict);
builder.Entity<AspNetRole>()
    .HasMany(e => e.AspNetUserRoles)
    .WithOne()
    .HasForeignKey(e => e.RoleId)
    .IsRequired()
    .OnDelete(DeleteBehavior.Restrict);

十分ではありません-逆も追加する必要があります:

builder.Entity<AspNetUserRole>()
    .HasOne(x => x.AspNetUser)
    .WithMany(x => x.AspNetUserRoles)
    .HasForeignKey(x => x.UserId);

builder.Entity<AspNetUserRole>()
    .HasOne(x => x.AspNetRole)
    .WithMany(x => x.AspNetUserRoles)
    .HasForeignKey(x => x.RoleId);
7

例外メッセージは明確ではありませんが、通常は不適切なモデル構成を示しています。

ここで考慮すべき要素がいくつかあります。

まず、バージョン2.0では、ナビゲーションプロパティがアイデンティティモデルから削除され、ベースのIndentityDbCOntext実装explicitlyが関係を構成せずにどちらかの側のナビゲーションプロパティ

最後は非常に重要です。 EF Coreは、規則、データの注釈、および(Fluent APIを介した)明示的な構成を使用します。規則は最低の優先順位で、明示的な構成は最高の優先順位です。つまり、データアノテーションは規則をオーバーライドできますが、明示的な構成はできません。明示的な構成は、規則とデータ注釈の両方と、以前の明示的な構成(最後の勝者)をオーバーライドできます。つまり、明示的な構成をオーバーライドする唯一の方法は、基本構成の後にFluent APIを使用することです。

モデルはいくつかのナビゲーションプロパティを追加するので、それを反映するように関係を再構成する必要があります。リレーションシップ構成の一般的な間違いは、実際にはモデルdoの場合、ナビゲーションプロパティ名/式を指定せずにHas/Withメソッドを使用することですナビゲーションプロパティがあります。論理的には、オプションの引数をスキップするとuse defaultを意味すると思いますが、ここでは実際にはナビゲーションプロパティがないことを意味します。これにより、次の予期しない動作が発生します。

ナビゲーションプロパティはEFによってまだ発見されています。それらは構成された関係の一部ではないため、EFはそれらをseparate関係の一部であると見なし、従来のようにデフォルトのシャドウFKプロパティ/列名でマップします。これは間違いなくあなたが望むものではありません。

関係を2回構成する必要はありません。実際には一度設定する方が良いですが、その最後にナビゲーションプロパティの有無を表す正しいWith/Has呼び出し引数を使用します。

そうは言っても、OnModelCreatingをオーバーライドし、基本実装を呼び出してから、次のものを追加して、アイデンティティモデルの派生エンティティに導入されたナビゲーションプロパティを反映させる必要があります。

builder.Entity<AspNetUserRole>()
    .HasOne(x => x.AspNetUser)
    .WithMany(x => x.AspNetUserRoles)
    .HasForeignKey(x => x.UserId);

builder.Entity<AspNetUserRole>()
    .HasOne(x => x.AspNetRole)
    .WithMany(x => x.AspNetUserRoles)
    .HasForeignKey(x => x.RoleId);

AspNetRole.AspNetRoleClaimsコレクションなどの他のナビゲーションプロパティについても同様です。詳細については、さまざまな関係構成を説明する Relationships EF Coreのドキュメントトピックを参照してください

また、デフォルトでは、IdentityUserRoleは(再び明示的に)複合PK({ UserId, RoleId })を使用するように構成され、派生したAspNetUserRoleエンティティは独自のPK(Id )、次のことも明示的に指定する必要があります。

builder.Entity<AspNetUserRole>()
    .HasKey(e => e.Id);
3
Ivan Stoev

私にとって、これは他の流暢なコードの前にベースのOnModelCreatingメソッドを呼び出すことで修正されました:

        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<UserDefinition>()
            .ToTable("UserDefinition");
        //    .HasOne(x => x.Subscription)
        //    .WithMany(x => x.UserDefinitions)
        //    .HasForeignKey(x => x.SubscriptionId);

        modelBuilder.Entity<Subscription>()
            .ToTable("Subscription");
        //    .HasMany(x => x.UserDefinitions)
        //    .WithOne()
        //    .HasForeignKey(x => x.SubscriptionId);

私はここで答えを見つけました EF Core 2.0 Identity-ナビゲーションプロパティの追加

1
Liam

idLongではなくintであると思うので、関係はlongではなくintデータ型の間である必要があります試してください!!

以前は、intデータ型を使用して関係を作成していましたが、EF移行では受け入れられませんでした。

0
Rabea AlTaradeh