EntityFrameworkCoreのOnModelCreating

12927 ワード

データベースORMフレームワークとしてEntityFrameworkCoreを使用する場合、DbContextの虚メソッドOnModelCreatingを再ロードすることは避けられませんが、このメソッドはいったい何をしているのでしょうか.いったいどんな役割があるのだろうか.これらの質問を持って、EntityFrameworkCoreでOnModelCreatingという方法をどのように使用するかを見てみましょう.まず、Microsoft.EntityFrameworkCoreネーミングスペースの下にあるDbContextのOnModelCreatingの定義と注釈を見てみましょう.
/// 
    ///     Override this method to further configure the model that was discovered by convention from the entity types
    ///     exposed in  properties on your derived context. The resulting model may be cached
    ///     and re-used for subsequent instances of your derived context.
    /// 
    /// 
    ///     If a model is explicitly set on the options for this context (via )
    ///     then this method will not be run.
    /// 
    /// 
    ///     The builder being used to construct the model for this context. Databases (and other extensions) typically
    ///     define extension methods on this object that allow you to configure aspects of the model that are specific
    ///     to a given database.
    /// 
    protected internal virtual void OnModelCreating(ModelBuilder modelBuilder)
    {
    }

公式の説明に従って、派生コンテキストのモデルの初期化が完了した後、モデルがロックされ、コンテキストの初期化に使用される前に、このメソッドが呼び出されます.このメソッドのデフォルトインプリメンテーションでは、アクションは実行されませんが、派生クラスでこのメソッドを書き換えることで、モデルをロックする前にさらに構成できます.通常、このメソッドは、派生コンテキストの最初のインスタンスを作成するときに一度だけ呼び出され、コンテキストのモデルがキャッシュされ、アプリケーションドメイン内のコンテキストのすべての後続インスタンスに適用されます.また、データベースの移行によって移行ファイルが生成されるときにOnModelCreatingメソッドも呼び出されます.このキャッシュは、指定したModelBuidlerにModelCachingプロパティを設定することで無効になりますが、パフォーマンスが大幅に低下することに注意してください.DbModelBuilderクラスとDbContextFactoryクラスを直接使用することで、キャッシュのより多くの制御を提供します.では、これらの深さのカスタム構成にはどのような面がありますか?
      Fluent APIによるエンティティの構成
この部分は全体の配置の重点で、私达はFluent APIを通じて各种の配置を行うことができて、以下は一连の例を通じて说明して、ここで1篇のとても良い文章を推荐して、ここではもう赘述しないで、ここでただ前の异なる部分だけを述べて、全体の文章を更に完备させて、もしあなたがデータベースの中の関系のマッピングに対してあまりはっきりしないならば、この文章を読んでください.
A主外部キー関係の設定
 #region Sales.Basis        

            modelBuilder.Entity()
                .HasOne(d => d.MarketingDepartment)
                .WithMany(e => e.DealerMarketDptRelations)
                .HasForeignKey(d => d.MarketId);

            modelBuilder.Entity()
                .HasOne(c => c.Branch)
                .WithOne(d => d.Company)
                .HasForeignKey(d => d.Id);

            modelBuilder.Entity()
                .HasOne(c => c.Dealer)
                .WithOne(d => d.Company)
                .HasForeignKey(d => d.Id);

            #endregion  

  B  索引の設定
 modelBuilder.Entity(r => {
                r.HasIndex(p => p.Code);
                r.HasIndex(p => p.Vin);
                r.HasIndex(p => p.DealerCode);
                r.HasIndex(p => p.DealerName);
                r.HasIndex(p => p.CreateTime);
                r.HasIndex(p => p.ModifyTime);
            });

データベースをクエリーするときにインデックスを追加することがよくあります.Fluent APIでは、上記の方法でテーブルにインデックスを追加できます.
C Sequenceの作成
            modelBuilder.HasSequence("S_PdiCheck");
            modelBuilder.HasSequence("S_PdiCheckItem");
            modelBuilder.HasSequence("S_PdiCheckItemDetail");
            modelBuilder.HasSequence("S_PdiCheckAth");
            modelBuilder.HasSequence("S_CancellationHandlingTime");
            modelBuilder.HasSequence("S_DealerBlacklist");
            modelBuilder.HasSequence("S_PropagateMethod");
            modelBuilder.HasSequence("S_VehicleOwnerChange");
            modelBuilder.HasSequence("S_CustomerVehicleAth");
            modelBuilder.HasSequence("S_DealerVehicleStockSnapshots");  

Dテーブルカスケード削除の設定
        /// 
        ///           
        ///          ,         
        /// 
        /// 
        public static void AdjustDbDefautAction(this ModelBuilder modelBuilder) {
            foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes()) {
                if (mutableEntityType.ClrType == null)
                    continue;
                if (mutableEntityType.ClrType.GetCustomAttribute() == null)
                    mutableEntityType.Relational().TableName = mutableEntityType.ClrType.Name;
                foreach (var foreignKey in mutableEntityType.GetForeignKeys()) {            
            // foreignKey.DeleteBehavior = DeleteBehavior.Restrict;
} } }  

しかし、ビジネスの必要性からカスケード削除を開く必要がある場合もあります.もちろん、次の例のようにOnDeleteメソッドで削除することができます.
modelBuilder.Entity(b => {
                    b.HasMany(rs => rs.RepairSettlementWorkItems)
                    .WithOne(rsi => rsi.RepairSettlement)
                    .OnDelete(DeleteBehavior.Cascade);
            });        

ここでは、1つのRepairSettingsエンティティに複数のRepairSet lementWorkItemエンティティを含めることができます.これは、RepairSettingsでリストを定義して両者の関係を表すことができ、OnDeleteメソッドで両者の間のカスケード削除関係を実現します.
しかし、システムにこのようなメインリスト関係が大量に存在する場合、カスケード削除が必要である場合、私たちのOnModelCreatingにはこのような重複コードが大量に存在するので、両者の間のカスケード削除関係を指定するのにもっと適切な方法はありませんか?
答えは、カスタムプロパティを使用して、エンティティの外部キーに直接カスケード削除関係を定義することで、コードを最大限に簡略化できます.
1 ForeignKeyReferenceAttributeの定義
  /// 
    ///                                
    /// 
    public class ForeignKeyReferenceAttribute : Attribute {
        /// 
        ///            
        /// 
        public ForeignKeyReferenceAttribute() {
            DeleteBehavior = DeleteBehavior.Restrict;
        }

        /// 
        ///              
        /// 
        public DeleteBehavior DeleteBehavior { get; set; }
    }

2カスケード削除の設定方法の変更
        /// 
        ///           
        ///          ,         
        /// 
        /// 
        public static void AdjustDbDefautAction(this ModelBuilder modelBuilder) {
            foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes()) {
                if (mutableEntityType.ClrType == null)
                    continue;
                if (mutableEntityType.ClrType.GetCustomAttribute() == null)
                    mutableEntityType.Relational().TableName = mutableEntityType.ClrType.Name;
                foreach (var foreignKey in mutableEntityType.GetForeignKeys()) {
                    var canCascadeDelete = foreignKey.Properties[0]?.PropertyInfo?.GetCustomAttributes()?.SingleOrDefault();
                    foreignKey.DeleteBehavior = canCascadeDelete?.DeleteBehavior ?? DeleteBehavior.Restrict;
                }
            }
        }  

ここではforeignKey.DeleteBehavior=DeleteBehavior.Restrictを統一的に設定しません.現在の外部キーで定義されているForeignKeyReferenceAttributeの値に基づいて、現在の外部キーのカスケード削除関係を決定します.
3外部キーのカスケード削除関係の設定
上記の例では、テーブルRepairSettings lementWorkItemの外部キーRepairSettings lementIdに外部キーカスケード削除関係を設定できます.
 [ForeignKeyReference(DeleteBehavior = DeleteBehavior.Cascade)]
 public Guid RepairSettlementId { get; set; }  

E異なるデータベース固有特性の設定
データベースにはさまざまなタイプがあるため、Oracleデータベースではカラム名を最大30文字に制限するなど、特定のデータベースに対して構成する必要があります.たとえば、LevelはOracleデータベースでキーワードとして使用される場合がありますが、別のデータベースシステムではこの制限はありません.したがって、modelBuilder.Entity(d=>d.Property(p=>p.Level).HasColumnName("FLevel"))のようにエンティティ属性名をFLevelに設定することで、OnModelCreatingでも一括して処理できます.
if (Database.IsOracle()) {
                // Oracle        30   
                modelBuilder.Entity(entity => {
                    entity.Property(e => e.EntityTypeAssemblyQualifiedName)
                        .HasColumnName("EntityTypeAssyQualifiedName");
                });
                modelBuilder.Entity(entity => {
                    entity.Property(e => e.EntityTypeAssemblyQualifiedName)
                        .HasColumnName("EntityTypeAssyQualifiedName");
                });
                modelBuilder.Entity(entity => {
                    entity.Property(e => e.EntityTypeAssemblyQualifiedName)
                        .HasColumnName("EntityTypeAssyQualifiedName");
                });

                //          
                modelBuilder.Entity(entity => {
                    entity.Property(e => e.ExecutionTime).HasColumnType("TimeStamp");
                });
                modelBuilder.Entity(entity => {
                    entity.Property(e => e.ChangeTime).HasColumnType("TimeStamp");
                });
                modelBuilder.Entity(entity => {
                    entity.Property(e => e.CreationTime).HasColumnType("TimeStamp");
                    entity.Property(e => e.ExtensionData).HasMaxLength(int.MaxValue);
                });

                foreach (var entity in modelBuilder.Model.GetEntityTypes()) {
                    if (entity.ClrType == null) continue;
                    if (!entity.ClrType.GetAttributes().Any()) {
                        entity.Relational().TableName = entity.ClrType.Name;
                    }
                    foreach (var property in entity.GetProperties().Where(p => p.PropertyInfo != null)){
                        if (property.PropertyInfo.Name.ToUpper().EndsWith("ID") && property.ClrType == typeof(string)) {
                            property.Relational().ColumnType = "CHAR(36)";
                        }
                        else if (property.Name == "RowVersion" &&
                                 (property.ClrType == typeof(DateTime) || property.ClrType == typeof(DateTime?))) {
                            property.Oracle().ColumnType = "TIMESTAMP";
                        }
                        else if (property.ClrType == typeof(decimal) || property.ClrType == typeof(decimal?)) {
                            property.Oracle().ColumnType = "NUMBER(16,4)";
                        }

                    }
                }
                modelBuilder.Entity(d => d.Property(p => p.FileId).HasColumnType("VARCHAR2(200)"));
                modelBuilder.Entity(d => d.Property(p => p.FileId).HasColumnType("VARCHAR2(200)"));
                modelBuilder.Entity(d => d.Property(p => p.Level).HasColumnName("FLevel"));
            }  

たとえば、テーブルでよく使用されるRowVersionフィールドは、OracleではエンティティをDateTimeタイプとして定義できますが、SQLサーバではbyte[]として定義できるだけなので、使用する場合は上記の方法で統一的に処理できます.
/// 
        /// dotConnect   DateTime    TimeStamp,      String   Clob
        /// 
        /// 
        public static void AdjustOracleDefaultAction(this ModelBuilder modelBuilder) {
            foreach (var item in modelBuilder.Model
                .GetEntityTypes()
                .SelectMany(t => t.GetProperties())
                .Where(p => p.ClrType == typeof(string) || NoneAnnotationDateTime(p))
                .Select(p => new {
                    p.ClrType,
                    p.Name,
                    Pb = modelBuilder.Entity(p.DeclaringEntityType.ClrType).Property(p.Name),
                    MaxLength = p.GetMaxLength()
                })) {
                if (item.ClrType == typeof(DateTime?)) {
                    item.Pb.HasColumnType("date");
                } else
                if (item.Name == "Discriminator")
                    item.Pb.HasMaxLength(100);
                // ReSharper disable once PossibleInvalidOperationException
                else if (item.ClrType == typeof(string) && item.MaxLength == null)
                    item.Pb.HasMaxLength(2000);
            }
        }

ここでは、いくつかの実際のプロジェクトの経験を通じて、EntityFrameworkCoreのいくつかの特性を説明し、その後、さらなる新しい内容が追加され、文章の内容をより豊かにします.
転載先:https://www.cnblogs.com/seekdream/p/10641510.html