.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);
例外メッセージは明確ではありませんが、通常は不適切なモデル構成を示しています。
ここで考慮すべき要素がいくつかあります。
まず、バージョン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);
私にとって、これは他の流暢なコードの前にベースの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-ナビゲーションプロパティの追加 。
id
はLong
ではなくint
であると思うので、関係はlong
ではなくint
データ型の間である必要があります試してください!!
以前は、int
データ型を使用して関係を作成していましたが、EF移行では受け入れられませんでした。