プロパティとしてのクラス(Table Per Type編) 2


前回からの考え直し(インターフェイスから抽象型)

前回ではDataTime型に限ったプロパティクラスを考えたが実際の運用考えると下記のような抽象ジェネリッククラスの方が運用しやすい。

CompornentEFcore.cs
    public abstract class CompornentClass<TEntity, DataType> where TEntity : class where DataType : struct
    {
        public CompornentClass() { value = default(DataType); }
        public CompornentClass(DataType value) { this.value = value; }
        public int iD { get; set; }
        public DataType value { get; set; }
        public TEntity CompornentSideNavigation { get; set; }
    }

何故インターフェイスから抽象ジェネリッククラスに置き換えたかというと、そもそもEntityFrameworkは1クラス、1テーブルなので1ジェネリッククラスで複数テーブルは無理あったためである。
実際運用する際にはここから継承して色々機能を加えていくことになる。

コンテキスト定義

さらに似たような型であるからコンテキスト定義も一括できるはずである。
そこでコンポジット側とコンポーネント側の定義を行う静的関数を定義する

ContextUtility.CompositeSide
        public static EntityTypeBuilder<TEntity> ConfigureCopositeSide<TEntity, TRelated, DataType>(
                        this EntityTypeBuilder<TEntity> EntityTypeBuilder,
            Expression<Func<TEntity, TRelated>> ConpositeSideProperty
            )
                        where TEntity : class where TRelated : CompornentClass<TEntity, DataType> where DataType : struct
        {
            EntityTypeBuilder.Navigation(ConpositeSideProperty);
            EntityTypeBuilder.HasOne(ConpositeSideProperty)
                .WithOne(CompornentClass => CompornentClass.CompornentSideNavigation);
            return EntityTypeBuilder;
        }

この関数で行っていることは
入力としてエンティティタイプビルダー、コンポジット側プロパティを受け取って
ナビゲーションプロパティとしてコンポーネント側を返す関数を定義して
ナビゲーションプロパティから1対1で関係性を定義している。
ジェネリックの型情報が3つに成っているが恐らくは型推定で補完されるだろう。

ContextUtility.CompornentSide
        public static EntityTypeBuilder<TRelated> ConfigureCompornentSide<TEntity, TRelated, DataType>(
            this EntityTypeBuilder<TRelated> EntityTypeBuilder,
            Expression<Func<TEntity, object>> ConpositeSideID,
            Expression<Func<TEntity, TRelated>> ConpositeSideProperty)
            where TEntity : class where TRelated : CompornentClass<TEntity, DataType> where DataType : struct
        {
            EntityTypeBuilder.ToTable(typeof(TEntity).ToString() + '_' + ConpositeSideProperty.Type.ToString() + "_Values");
            EntityTypeBuilder.HasKey(d => d.iD);
            EntityTypeBuilder.Property(d => d.value).IsRequired();
            EntityTypeBuilder.Navigation(d => d.CompornentSideNavigation);
            EntityTypeBuilder.HasOne(d => d.CompornentSideNavigation)
                .WithOne(ConpositeSideProperty)
                   .HasForeignKey(ConpositeSideID);
            return EntityTypeBuilder;
        }

こちらも同様である。
入力としてエンティティタイプビルダー、コンポジット側ID、コンポジット側プロパティを受け取って
主キー、プロパティ、ナビゲーション、1対1の関係性を定義しているだけである。

今回の成果はGithubで公開中
https://github.com/Nishiwaki-Takao/CompornentEFcore